设备相关
This commit is contained in:
@@ -65,6 +65,10 @@
|
||||
android:name=".login.ForgetPsdActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".device.BindDeviceActivity"
|
||||
android:exported="false" />
|
||||
|
||||
|
||||
|
||||
<provider
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.juhabi.birds.custom;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.juhabi.birds.R;
|
||||
import com.scwang.smart.refresh.layout.api.RefreshHeader;
|
||||
import com.scwang.smart.refresh.layout.api.RefreshKernel;
|
||||
import com.scwang.smart.refresh.layout.api.RefreshLayout;
|
||||
import com.scwang.smart.refresh.layout.constant.RefreshState;
|
||||
import com.scwang.smart.refresh.layout.constant.SpinnerStyle;
|
||||
|
||||
/**
|
||||
* @author fengxiaoyang
|
||||
* @date 2025/7/11
|
||||
* @description TODO: 描述该类的作用
|
||||
*/
|
||||
|
||||
public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader {
|
||||
|
||||
private ImageView ivRefresh;
|
||||
private ObjectAnimator rotateAnimator;
|
||||
// 用于 onMoving 中的动态效果
|
||||
private float mLastPercent = 0f;
|
||||
// 记录当前缩放值,避免重复设置
|
||||
private float mCurrentScale = 1f;
|
||||
|
||||
public CustomRefreshHeader(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CustomRefreshHeader(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_refresh_header, this);
|
||||
ivRefresh = findViewById(R.id.ivRefresh);
|
||||
|
||||
// 开启硬件加速,提升动画性能
|
||||
setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
// 初始化 ObjectAnimator
|
||||
rotateAnimator = ObjectAnimator.ofFloat(ivRefresh, "rotation", 0f, 360f);
|
||||
rotateAnimator.setDuration(1500);
|
||||
rotateAnimator.setRepeatCount(ValueAnimator.INFINITE);
|
||||
rotateAnimator.setInterpolator(new LinearInterpolator());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onStartAnimator(@NonNull RefreshLayout layout, int height, int maxDragHeight) {
|
||||
if (rotateAnimator != null && !rotateAnimator.isRunning()) {
|
||||
rotateAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public int onFinish(@NonNull RefreshLayout layout, boolean success) {
|
||||
if (rotateAnimator != null && rotateAnimator.isRunning()) {
|
||||
rotateAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
postDelayed(() -> {
|
||||
if (rotateAnimator != null) {
|
||||
rotateAnimator.cancel();
|
||||
rotateAnimator.setInterpolator(new LinearInterpolator());
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
// 动画过渡回正常大小
|
||||
animateToNormalScale();
|
||||
return 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑动画过渡回正常大小
|
||||
*/
|
||||
private void animateToNormalScale() {
|
||||
if (ivRefresh == null) return;
|
||||
|
||||
float startScale = mCurrentScale;
|
||||
ValueAnimator scaleAnimator = ValueAnimator.ofFloat(startScale, 1f);
|
||||
scaleAnimator.setDuration(300);
|
||||
scaleAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
scaleAnimator.addUpdateListener(animation -> {
|
||||
float value = (float) animation.getAnimatedValue();
|
||||
ivRefresh.setScaleX(value);
|
||||
ivRefresh.setScaleY(value);
|
||||
mCurrentScale = value;
|
||||
});
|
||||
scaleAnimator.start();
|
||||
}
|
||||
|
||||
// --- 必须实现的方法 ---
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SpinnerStyle getSpinnerStyle() {
|
||||
return SpinnerStyle.Translate; // Header 跟随内容滑动
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupportHorizontalDrag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void setPrimaryColors(@ColorInt int... colors) {}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
|
||||
// 只在拖动过程中处理,避免过度计算
|
||||
if (percent != mLastPercent) {
|
||||
mLastPercent = percent;
|
||||
|
||||
// 1. 动态缩放效果:从 0.5 放大到 1.2
|
||||
// 使用 clamp 限制范围:percent 0~1 -> scale 0.5~1.2
|
||||
float targetScale = 0.5f + (0.7f * Math.min(percent, 1.0f));
|
||||
if (Math.abs(targetScale - mCurrentScale) > 0.01f) {
|
||||
mCurrentScale = targetScale;
|
||||
ivRefresh.setScaleX(targetScale);
|
||||
ivRefresh.setScaleY(targetScale);
|
||||
}
|
||||
|
||||
// 2. 透明度渐变效果(可选)
|
||||
// ivRefresh.setAlpha(Math.min(percent * 1.5f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onReleased(@NonNull RefreshLayout layout, int height, int maxDragHeight) {}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {}
|
||||
|
||||
@Override
|
||||
public boolean autoOpen(int duration, float dragRate, boolean animationOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
|
||||
// 状态切换时平滑恢复
|
||||
if (newState == RefreshState.PullDownCanceled) {
|
||||
// 用户取消下拉,恢复到初始状态
|
||||
animateToNormalScale();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.juhabi.birds.device
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import com.juhabi.birds.R
|
||||
import com.juhabi.birds.base.BaseActivity
|
||||
import com.juhabi.birds.databinding.BindDeviceLayoutBinding
|
||||
import com.juhabi.birds.device.entity.DeviceEntity
|
||||
import com.juhabi.birds.device.mvp.DeviceContract
|
||||
import com.juhabi.birds.device.mvp.DeviceImpl
|
||||
import com.juhabi.birds.device.mvp.DevicePresenter
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* 绑定设备
|
||||
*/
|
||||
class BindDeviceActivity: BaseActivity<BindDeviceLayoutBinding, DevicePresenter>() , DeviceContract.DeviceView,
|
||||
View.OnClickListener {
|
||||
override fun createBinding(inflater: LayoutInflater): BindDeviceLayoutBinding {
|
||||
return BindDeviceLayoutBinding.inflate(inflater)
|
||||
}
|
||||
|
||||
override fun createPresenter(): DevicePresenter? {
|
||||
return DevicePresenter(DeviceImpl())
|
||||
}
|
||||
|
||||
override fun initWindows() {
|
||||
ImmersionBar.with(this)
|
||||
.statusBarView(binding.statusBarView)
|
||||
.statusBarDarkFont(true)
|
||||
.navigationBarColor(R.color.color_zhu)
|
||||
.navigationBarDarkIcon(true)
|
||||
.init()
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
binding.includedTitle.tvTitle.setText(R.string.bind_device)
|
||||
listOf(
|
||||
binding.tvBind,
|
||||
binding.includedTitle.imgBack,
|
||||
).forEach { button ->
|
||||
button.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.img_back ->{
|
||||
finish()
|
||||
}
|
||||
|
||||
R.id.tv_bind ->{//绑定设备
|
||||
// val imei = "${System.currentTimeMillis()}${Random.nextInt(100).toString().padStart(2, '0')}"
|
||||
// val name = "device001"
|
||||
val imei = "860000000000005"
|
||||
val name = "BM202609160005"
|
||||
presenter?.bindDevice(imei, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showError(message: String) {
|
||||
showMessage(message)
|
||||
}
|
||||
|
||||
override fun getBindDeviceListSuccess(devices: List<DeviceEntity>) {
|
||||
}
|
||||
|
||||
/** 绑定设备成功后提示用户并关闭当前页面。 */
|
||||
override fun bindDeviceSuccess(message: String) {
|
||||
showMessage(message)
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.juhabi.birds.device
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import com.juhabi.birds.base.BaseActivity
|
||||
import com.juhabi.birds.databinding.BindDeviceLayoutBinding
|
||||
import com.juhabi.birds.device.entity.DeviceEntity
|
||||
import com.juhabi.birds.device.mvp.DeviceContract
|
||||
import com.juhabi.birds.device.mvp.DeviceImpl
|
||||
import com.juhabi.birds.device.mvp.DevicePresenter
|
||||
|
||||
/**
|
||||
* 设备详情
|
||||
*/
|
||||
class DeviceDetailActivity: BaseActivity<BindDeviceLayoutBinding, DevicePresenter>() , DeviceContract.DeviceView,
|
||||
View.OnClickListener {
|
||||
override fun createBinding(inflater: LayoutInflater): BindDeviceLayoutBinding {
|
||||
return BindDeviceLayoutBinding.inflate(inflater)
|
||||
}
|
||||
|
||||
override fun createPresenter(): DevicePresenter? {
|
||||
return DevicePresenter(DeviceImpl())
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
}
|
||||
|
||||
override fun showError(message: String) {
|
||||
}
|
||||
|
||||
override fun getBindDeviceListSuccess(devices: List<DeviceEntity>) {
|
||||
}
|
||||
|
||||
override fun bindDeviceSuccess(message: String) {
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
}
|
||||
}
|
||||
184
app/src/main/java/com/juhabi/birds/device/IndexFragment.kt
Normal file
184
app/src/main/java/com/juhabi/birds/device/IndexFragment.kt
Normal file
@@ -0,0 +1,184 @@
|
||||
package com.juhabi.birds.device
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.juhabi.birds.R
|
||||
import com.juhabi.birds.base.BaseActivity
|
||||
import com.juhabi.birds.base.BaseFragment
|
||||
import com.juhabi.birds.custom.DeviceActionPopupWindow
|
||||
import com.juhabi.birds.custom.CustomRefreshHeader
|
||||
import com.juhabi.birds.databinding.FragmentHomeBinding
|
||||
import com.juhabi.birds.device.entity.DeviceEntity
|
||||
import com.juhabi.birds.device.mvp.DeviceContract
|
||||
import com.juhabi.birds.device.mvp.DeviceImpl
|
||||
import com.juhabi.birds.device.mvp.DevicePresenter
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomeContract
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomeImpl
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomePresenter
|
||||
import com.scwang.smart.refresh.footer.ClassicsFooter
|
||||
|
||||
class IndexFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
|
||||
DeviceContract.DeviceView, View.OnClickListener {
|
||||
|
||||
private val deviceAdapter = DeviceAdapter()
|
||||
private var currentPage = FIRST_PAGE
|
||||
private var pendingUnbindPosition: Int? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeBinding {
|
||||
return FragmentHomeBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun createPresenter(): DevicePresenter {
|
||||
return DevicePresenter(DeviceImpl())
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
listOf(
|
||||
binding.tvIndex,
|
||||
).forEach { button ->
|
||||
button.setOnClickListener(this)
|
||||
}
|
||||
binding.rvDevices.adapter = deviceAdapter
|
||||
deviceAdapter.setOnItemChildClickListener { adapter, view, position ->
|
||||
when (view.id) {
|
||||
R.id.img_tips -> {
|
||||
val device = deviceAdapter.data[position]
|
||||
DeviceActionPopupWindow(requireContext()).show(
|
||||
view,
|
||||
onRename = {//重命名
|
||||
showEditDialog(
|
||||
"设备重命名", device.name, click = object : OnDialogClick {
|
||||
|
||||
override fun onCancelClick() {}
|
||||
override fun onConfirmClick(content: String) {
|
||||
presenter?.renameDevice(device.deviceNo, content)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
onUnbind = {//解绑设备
|
||||
showDialogtext(
|
||||
"提示",
|
||||
"确定要解绑该设备?",
|
||||
"确定",
|
||||
"取消",
|
||||
object : OnDialogClick {
|
||||
override fun onCancelClick() {
|
||||
}
|
||||
|
||||
override fun onConfirmClick() {
|
||||
// 用户确认后,按设备编号请求解绑
|
||||
pendingUnbindPosition = position
|
||||
presenter?.unbindDevice(device.deviceNo)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun initData(savedInstanceState: Bundle?) {
|
||||
super.initData(savedInstanceState)
|
||||
binding.refresh.setRefreshHeader(CustomRefreshHeader(requireContext()))
|
||||
binding.refresh.setRefreshFooter(ClassicsFooter(requireContext()))
|
||||
binding.refresh.setOnRefreshListener {
|
||||
currentPage = FIRST_PAGE
|
||||
requestDeviceList()
|
||||
}
|
||||
binding.refresh.setOnLoadMoreListener {
|
||||
currentPage++
|
||||
requestDeviceList()
|
||||
}
|
||||
requestDeviceList()
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when(view.id){
|
||||
R.id.tv_index ->{
|
||||
goTo(BindDeviceActivity::class.java,null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBindDeviceListSuccess(devices: List<DeviceEntity>) {
|
||||
if (currentPage == FIRST_PAGE) {
|
||||
deviceAdapter.setList(devices)
|
||||
} else {
|
||||
deviceAdapter.addData(devices)
|
||||
}
|
||||
binding.refresh.finishRefresh()
|
||||
binding.refresh.finishLoadMore()
|
||||
binding.refresh.setNoMoreData(devices.size < PAGE_SIZE)
|
||||
binding.relNoMore.visibility =
|
||||
if (deviceAdapter.data.isEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun bindDeviceSuccess(message: String) {
|
||||
|
||||
}
|
||||
|
||||
/** 设备重命名成功后提示用户并重新加载第一页数据。 */
|
||||
override fun renameDeviceSuccess(message: String) {
|
||||
showMessage(message)
|
||||
currentPage = FIRST_PAGE
|
||||
requestDeviceList()
|
||||
}
|
||||
|
||||
/** 设备解绑成功后先播放删除动画,再延迟刷新第一页数据。 */
|
||||
override fun unbindDeviceSuccess(message: String) {
|
||||
showMessage(message)
|
||||
val removedPosition = pendingUnbindPosition
|
||||
pendingUnbindPosition = null
|
||||
if (removedPosition != null && removedPosition in deviceAdapter.data.indices) {
|
||||
deviceAdapter.data.removeAt(removedPosition)
|
||||
deviceAdapter.notifyItemRemoved(removedPosition)
|
||||
binding.relNoMore.visibility =
|
||||
if (deviceAdapter.data.isEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
// 等待 RecyclerView 删除动画完成后再刷新,避免刷新结果立即覆盖动画。
|
||||
binding.root.postDelayed({
|
||||
if (!isAdded) return@postDelayed
|
||||
currentPage = FIRST_PAGE
|
||||
requestDeviceList()
|
||||
}, REMOVE_ANIMATION_DELAY_MS)
|
||||
}
|
||||
|
||||
/** 请求指定页的设备列表。 */
|
||||
private fun requestDeviceList() {
|
||||
presenter?.getBindDeviceList(
|
||||
mapOf(
|
||||
"pageNum" to currentPage.toString(),
|
||||
"pageSize" to PAGE_SIZE.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** 请求失败时结束刷新状态并恢复上一次有效页码。 */
|
||||
override fun showError(message: String) {
|
||||
binding.refresh.finishRefresh(false)
|
||||
binding.refresh.finishLoadMore(false)
|
||||
if (currentPage > FIRST_PAGE) {
|
||||
currentPage--
|
||||
}
|
||||
super.showError(message)
|
||||
}
|
||||
|
||||
/** 接口暂无数据时提供临时设备,方便先验收列表布局。 */
|
||||
private fun temporaryDevices(): List<DeviceEntity> = listOf(
|
||||
DeviceEntity("暂无地址", "BM202609160005", null, "0", "已断开", "860000000000005", null, null, null, "BM202609160005", null, 0, "离线", "1"),
|
||||
DeviceEntity("暂无地址", "BM202609160006", null, "0", "已断开", "860000000000006", null, null, null, "BM202609160006", null, 0, "离线", "1"),
|
||||
DeviceEntity("暂无地址", "BM202609160007", null, "0", "已断开", "860000000000007", null, null, null, "BM202609160007", null, 0, "离线", "1")
|
||||
)
|
||||
|
||||
private companion object {
|
||||
const val FIRST_PAGE = 1
|
||||
const val PAGE_SIZE = 10
|
||||
const val REMOVE_ANIMATION_DELAY_MS = 300L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.juhabi.birds.device.entity
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class DeviceEntity(
|
||||
val address: String,
|
||||
val deviceNo: String,
|
||||
val hoodBattery: Int?,
|
||||
val hoodConnected: String,
|
||||
val hoodConnectedText: String,
|
||||
val imei: String,
|
||||
val lat: Double?,
|
||||
val lng: Double?,
|
||||
val model: String?,
|
||||
val name: String,
|
||||
val ringBattery: Int?,
|
||||
val signalLevel: Int,
|
||||
val signalText: String,
|
||||
val status: String
|
||||
) : Serializable
|
||||
@@ -15,6 +15,7 @@ import com.juhabi.birds.login.model.UserEntity
|
||||
import com.juhabi.birds.login.mvp.LoginContract
|
||||
import com.juhabi.birds.login.mvp.LoginImpl
|
||||
import com.juhabi.birds.login.mvp.LoginPresenter
|
||||
import com.juhabi.birds.main.MainActivity
|
||||
import com.juhabi.birds.mine.MineCenterActivity
|
||||
import com.juhabi.birds.network.TokenManager
|
||||
import com.juhabi.birds.util.SharedPreferencesUtil
|
||||
@@ -191,7 +192,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
|
||||
SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN)
|
||||
|
||||
ToastUtil.showShort(mContext, "登录成功")
|
||||
startActivity(Intent(this, MineCenterActivity::class.java))
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package com.juhabi.birds.login.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.io.Serializable
|
||||
|
||||
data class UserEntity(
|
||||
val scope: Any? = null,
|
||||
val openid: Any? = null,
|
||||
@SerializedName("access_token")
|
||||
val token: String = "",
|
||||
|
||||
)
|
||||
val token: String
|
||||
) : Serializable
|
||||
|
||||
@@ -10,8 +10,8 @@ import com.juhabi.birds.base.BaseActivity
|
||||
import com.juhabi.birds.databinding.ActivityMainBinding
|
||||
import com.juhabi.birds.main.adapter.MainPagerAdapter
|
||||
import com.juhabi.birds.main.fragment.HistoryFragment
|
||||
import com.juhabi.birds.main.fragment.HomeFragment
|
||||
import com.juhabi.birds.main.fragment.MineFragment
|
||||
import com.juhabi.birds.device.IndexFragment
|
||||
import com.juhabi.birds.mine.MineFragment
|
||||
import com.juhabi.birds.main.fragment.PlanFragment
|
||||
import com.juhabi.birds.main.model.AppVersionInfo
|
||||
import com.juhabi.birds.main.mvp.MainContract
|
||||
@@ -42,7 +42,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
binding.viewPager.adapter = MainPagerAdapter(
|
||||
this,
|
||||
listOf(HomeFragment(), PlanFragment(), HistoryFragment(), MineFragment())
|
||||
listOf(IndexFragment(), PlanFragment(), HistoryFragment(), MineFragment())
|
||||
)
|
||||
binding.viewPager.offscreenPageLimit = 1
|
||||
binding.viewPager.registerOnPageChangeCallback(object : androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback() {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.juhabi.birds.main.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.juhabi.birds.base.BaseFragment
|
||||
import com.juhabi.birds.databinding.FragmentHomeBinding
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomeContract
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomeImpl
|
||||
import com.juhabi.birds.main.fragment.home.mvp.HomePresenter
|
||||
|
||||
class HomeFragment : BaseFragment<FragmentHomeBinding, HomePresenter>(),
|
||||
HomeContract.HomeView {
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeBinding {
|
||||
return FragmentHomeBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun createPresenter(): HomePresenter {
|
||||
return HomePresenter(HomeImpl())
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.juhabi.birds.main.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.juhabi.birds.base.BaseFragment
|
||||
import com.juhabi.birds.databinding.FragmentMineBinding
|
||||
import com.juhabi.birds.main.fragment.mine.mvp.MineFragmentContract
|
||||
import com.juhabi.birds.main.fragment.mine.mvp.MineFragmentImpl
|
||||
import com.juhabi.birds.main.fragment.mine.mvp.MineFragmentPresenter
|
||||
import com.juhabi.birds.mine.MineCenterActivity
|
||||
|
||||
class MineFragment : BaseFragment<FragmentMineBinding, MineFragmentPresenter>(),
|
||||
MineFragmentContract.MineView {
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentMineBinding {
|
||||
return FragmentMineBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun createPresenter(): MineFragmentPresenter {
|
||||
return MineFragmentPresenter(MineFragmentImpl())
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
binding.openComponentCenterButton.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), MineCenterActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
50
app/src/main/java/com/juhabi/birds/mine/MineFragment.kt
Normal file
50
app/src/main/java/com/juhabi/birds/mine/MineFragment.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.juhabi.birds.mine
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.juhabi.birds.base.BaseFragment
|
||||
import com.juhabi.birds.databinding.FragmentMineBinding
|
||||
import com.juhabi.birds.mine.model.MineUserInfo
|
||||
import com.juhabi.birds.main.model.AppVersionInfo
|
||||
import com.juhabi.birds.mine.mvp.MineContract
|
||||
import com.juhabi.birds.mine.mvp.MineImpl
|
||||
import com.juhabi.birds.mine.mvp.MinePresenter
|
||||
|
||||
/**
|
||||
* 个人中心
|
||||
*/
|
||||
class MineFragment : BaseFragment<FragmentMineBinding, MinePresenter>(),
|
||||
MineContract.MineView {
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentMineBinding {
|
||||
return FragmentMineBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun createPresenter(): MinePresenter {
|
||||
return MinePresenter(MineImpl())
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
/** 页面恢复显示时重新请求当前用户的个人信息。 */
|
||||
presenter?.getUserInfo()
|
||||
}
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
}
|
||||
|
||||
/** 将个人信息请求结果更新到个人中心页面。 */
|
||||
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
|
||||
showMessage("个人信息获取成功")
|
||||
}
|
||||
|
||||
/** 版本检测回调,个人中心暂不处理版本信息。 */
|
||||
override fun onVersionChecked(versionInfo: AppVersionInfo) {
|
||||
}
|
||||
|
||||
/** 显示获取个人信息失败的原因。 */
|
||||
override fun showError(message: String) {
|
||||
showMessage(message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.juhabi.birds.mine.model
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
|
||||
data class UserInfoEntity(
|
||||
val createTime: String,
|
||||
val email: String,
|
||||
val nickName: String,
|
||||
val phoneBound: Boolean,
|
||||
val phoneNumber: String,
|
||||
val userName: String,
|
||||
val userType: String
|
||||
): Serializable
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.juhabi.birds.network
|
||||
|
||||
import com.juhabi.birds.device.entity.DeviceEntity
|
||||
import com.juhabi.birds.login.model.UserEntity
|
||||
import com.juhabi.birds.login.model.SendCodeEntity
|
||||
import com.juhabi.birds.main.model.AppVersionInfo
|
||||
@@ -7,10 +8,13 @@ import com.juhabi.birds.mine.model.MineUserInfo
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.HeaderMap
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.QueryMap
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
* 项目统一接口定义。所有业务接口地址集中维护在这里。
|
||||
@@ -115,8 +119,57 @@ interface ApiService {
|
||||
fun forgetPsdByEmail(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
|
||||
|
||||
|
||||
/**
|
||||
*查询设备列表
|
||||
*/
|
||||
@GET("app/device/list")
|
||||
fun getBindDeviceList(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<List<DeviceEntity>>>
|
||||
|
||||
@GET("app/v1/getUserInfo")
|
||||
|
||||
/**
|
||||
* 绑定设备
|
||||
{
|
||||
"imei": "860000000000007",
|
||||
"name": "001设备"
|
||||
}
|
||||
*/
|
||||
@POST("app/device/bind")
|
||||
fun bindDevice(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<Any>>
|
||||
|
||||
/**
|
||||
* 设备重新命名
|
||||
{
|
||||
"deviceNo": "BM202609160007",
|
||||
"name": "改名001"
|
||||
}
|
||||
}
|
||||
*/
|
||||
@PUT("app/device/{deviceNo}/name")
|
||||
fun deviceRename(@HeaderMap headers: Map<String, String>, @Path("deviceNo") deviceNo: String, @Body requestBody: RequestBody): Call<BaseBean<Any>>
|
||||
|
||||
|
||||
/**
|
||||
* 眼罩控制
|
||||
{
|
||||
"deviceNo": "BM202609160007",
|
||||
"coverOn": true
|
||||
}
|
||||
*/
|
||||
@PUT("app/device/{deviceNo}/hood")
|
||||
fun controlEye(@HeaderMap headers: Map<String, String>, @Path("deviceNo") deviceNo: String, @Body requestBody: RequestBody): Call<BaseBean<Any>>
|
||||
|
||||
|
||||
/**
|
||||
* 解绑设备
|
||||
*/
|
||||
@DELETE("app/device/{deviceNo}")
|
||||
fun unbindDevice(@HeaderMap headers: Map<String, String>, @Path("deviceNo") deviceNo: String): Call<BaseBean<Any>>
|
||||
|
||||
|
||||
/**
|
||||
* 获取个人信息
|
||||
*/
|
||||
@GET("app/user/profile")
|
||||
fun getMineUserInfo(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<MineUserInfo>>
|
||||
|
||||
|
||||
|
||||
28
app/src/main/res/layout/bind_device_layout.xml
Normal file
28
app/src/main/res/layout/bind_device_layout.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<View
|
||||
android:id="@+id/statusBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/color_zhu" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/rl_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/included_title"
|
||||
layout="@layout/include_activity_back_gray_layout" />
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/tv_bind"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:textSize="15sp"
|
||||
android:text="绑定"
|
||||
android:background="@color/color_zhu"/>
|
||||
</LinearLayout>
|
||||
80
app/src/main/res/layout/device_item.xml
Normal file
80
app/src/main/res/layout/device_item.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="#FFFFFF"
|
||||
android:elevation="2dp"
|
||||
android:padding="14dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end">
|
||||
<ImageView
|
||||
android:id="@+id/img_tips"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/control_eye"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@mipmap/icon_login_show"
|
||||
android:layout_gravity="end"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#222222"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="#2E8B57"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_no"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="#555555"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_imei"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="#777777"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="#777777"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_device_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="#777777"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/page_background">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@color/page_background"
|
||||
android:orientation="vertical">
|
||||
<RelativeLayout
|
||||
android:id="@+id/rl_title"
|
||||
android:layout_width="match_parent"
|
||||
@@ -12,10 +15,39 @@
|
||||
layout="@layout/include_title_home_layout" />
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/tv_index"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/home_page_title"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="24sp" />
|
||||
</FrameLayout>
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<!-- 设备列表区域 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="15dp"
|
||||
tools:itemCount="5"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rel_no_more"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" >
|
||||
<include
|
||||
android:id="@+id/lay_no_more"
|
||||
layout="@layout/layout_no_more"/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
</LinearLayout>
|
||||
|
||||
41
app/src/main/res/layout/layout_no_more.xml
Normal file
41
app/src/main/res/layout/layout_no_more.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/layout_no_more"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
tools:visibility="visible">
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/color_zhu" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="1dp"
|
||||
android:text="@string/no_more_data"
|
||||
android:textColor="@color/color_zhu"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/color_zhu" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
11
app/src/main/res/layout/view_refresh_header.xml
Normal file
11
app/src/main/res/layout/view_refresh_header.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:gravity="center"
|
||||
android:background="@color/transparent">
|
||||
<ImageView
|
||||
android:id="@+id/ivRefresh"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@mipmap/icon_water_head_refresh" />
|
||||
</RelativeLayout>
|
||||
@@ -7,11 +7,13 @@
|
||||
<item
|
||||
android:id="@+id/navigation_plan"
|
||||
android:icon="@drawable/ic_nav_plan"
|
||||
android:title="@string/tab_plan" />
|
||||
android:title="@string/tab_plan"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/navigation_history"
|
||||
android:icon="@drawable/ic_nav_history"
|
||||
android:title="@string/tab_history" />
|
||||
android:title="@string/tab_history"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/navigation_mine"
|
||||
android:icon="@drawable/ic_nav_mine"
|
||||
|
||||
BIN
app/src/main/res/mipmap-hdpi/icon_water_head_refresh.png
Normal file
BIN
app/src/main/res/mipmap-hdpi/icon_water_head_refresh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/icon_water_head_refresh.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/icon_water_head_refresh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/icon_water_head_refresh.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/icon_water_head_refresh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
@@ -7,6 +7,7 @@
|
||||
<string name="login_password">密码</string>
|
||||
<string name="login_button">登录</string>
|
||||
<string name="login_register">手机注册</string>
|
||||
<string name="bind_device">绑定设备</string>
|
||||
<string name="register_account_hint">请输入账号</string>
|
||||
<string name="register_password_hint">请输入密码</string>
|
||||
<string name="register_show_password">显示密码</string>
|
||||
@@ -18,6 +19,7 @@
|
||||
<string name="register_prompt">没有账号?立即注册</string>
|
||||
<string name="register_get_code">获取验证码</string>
|
||||
<string name="register_code_countdown">%1$d秒后重新获取</string>
|
||||
<string name="no_more_data">已经到底了</string>
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +65,8 @@
|
||||
<string name="tab_history">历史</string>
|
||||
<string name="tab_mine">我的</string>
|
||||
<string name="home_page_title">首页</string>
|
||||
<string name="device_action_rename">重命名</string>
|
||||
<string name="device_action_unbind">解绑设备</string>
|
||||
<string name="plan_page_title">计划</string>
|
||||
<string name="history_page_title">历史</string>
|
||||
<string name="mine_page_title">我的</string>
|
||||
|
||||
Reference in New Issue
Block a user