From 981cfae71c03f7336a07ce6b0750677e3fe4f14a Mon Sep 17 00:00:00 2001 From: fengjignqi <15192412330@163.com> Date: Sat, 19 Sep 2026 08:41:53 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 4 + app/src/main/AndroidManifest.xml | 4 + .../birds/custom/CustomRefreshHeader.java | 178 +++++++++++++++++ .../juhabi/birds/device/BindDeviceActivity.kt | 78 ++++++++ .../birds/device/DeviceDetailActivity.kt | 40 ++++ .../com/juhabi/birds/device/IndexFragment.kt | 184 ++++++++++++++++++ .../birds/device/entity/DeviceEntity.kt | 20 ++ .../com/juhabi/birds/login/LoginActivity.kt | 3 +- .../juhabi/birds/login/model/UserEntity.kt | 9 +- .../com/juhabi/birds/main/MainActivity.kt | 6 +- .../birds/main/fragment/HomeFragment.kt | 25 --- .../birds/main/fragment/MineFragment.kt | 30 --- .../com/juhabi/birds/mine/MineFragment.kt | 50 +++++ .../juhabi/birds/mine/model/UserInfoEntity.kt | 14 ++ .../com/juhabi/birds/network/ApiService.kt | 55 +++++- .../main/res/layout/bind_device_layout.xml | 28 +++ app/src/main/res/layout/device_item.xml | 80 ++++++++ app/src/main/res/layout/fragment_home.xml | 40 +++- app/src/main/res/layout/layout_no_more.xml | 41 ++++ .../main/res/layout/view_refresh_header.xml | 11 ++ .../main/res/menu/bottom_navigation_menu.xml | 6 +- .../mipmap-hdpi/icon_water_head_refresh.png | Bin 0 -> 5449 bytes .../mipmap-xxhdpi/icon_water_head_refresh.png | Bin 0 -> 5449 bytes .../icon_water_head_refresh.png | Bin 0 -> 9314 bytes app/src/main/res/values/strings.xml | 4 + gradle.properties | 1 + 26 files changed, 839 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/com/juhabi/birds/custom/CustomRefreshHeader.java create mode 100644 app/src/main/java/com/juhabi/birds/device/BindDeviceActivity.kt create mode 100644 app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt create mode 100644 app/src/main/java/com/juhabi/birds/device/IndexFragment.kt create mode 100644 app/src/main/java/com/juhabi/birds/device/entity/DeviceEntity.kt delete mode 100644 app/src/main/java/com/juhabi/birds/main/fragment/HomeFragment.kt delete mode 100644 app/src/main/java/com/juhabi/birds/main/fragment/MineFragment.kt create mode 100644 app/src/main/java/com/juhabi/birds/mine/MineFragment.kt create mode 100644 app/src/main/java/com/juhabi/birds/mine/model/UserInfoEntity.kt create mode 100644 app/src/main/res/layout/bind_device_layout.xml create mode 100644 app/src/main/res/layout/device_item.xml create mode 100644 app/src/main/res/layout/layout_no_more.xml create mode 100644 app/src/main/res/layout/view_refresh_header.xml create mode 100644 app/src/main/res/mipmap-hdpi/icon_water_head_refresh.png create mode 100644 app/src/main/res/mipmap-xxhdpi/icon_water_head_refresh.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/icon_water_head_refresh.png diff --git a/app/build.gradle b/app/build.gradle index 2707a18..e009301 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,4 +66,8 @@ dependencies { androidTestImplementation libs.androidx.junit //屏幕适配 implementation 'me.jessyan:autosize:1.2.1' + //下拉刷新,上拉加载 + implementation 'io.github.scwang90:refresh-layout-kernel:2.1.1' + implementation 'io.github.scwang90:refresh-header-classics:2.1.1' + implementation 'io.github.scwang90:refresh-footer-classics:2.1.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8404ea2..9e1cb4d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -65,6 +65,10 @@ android:name=".login.ForgetPsdActivity" android:exported="false" /> + + { + 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(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/juhabi/birds/device/BindDeviceActivity.kt b/app/src/main/java/com/juhabi/birds/device/BindDeviceActivity.kt new file mode 100644 index 0000000..bf5af59 --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/device/BindDeviceActivity.kt @@ -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() , 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) { + } + + /** 绑定设备成功后提示用户并关闭当前页面。 */ + override fun bindDeviceSuccess(message: String) { + showMessage(message) + finish() + } + + +} diff --git a/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt new file mode 100644 index 0000000..758de85 --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt @@ -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() , 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) { + } + + override fun bindDeviceSuccess(message: String) { + } + + override fun onClick(view: View) { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/juhabi/birds/device/IndexFragment.kt b/app/src/main/java/com/juhabi/birds/device/IndexFragment.kt new file mode 100644 index 0000000..795e720 --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/device/IndexFragment.kt @@ -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(), + 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) { + 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 = 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 + } + +} diff --git a/app/src/main/java/com/juhabi/birds/device/entity/DeviceEntity.kt b/app/src/main/java/com/juhabi/birds/device/entity/DeviceEntity.kt new file mode 100644 index 0000000..a6ccb1e --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/device/entity/DeviceEntity.kt @@ -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 diff --git a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt index 2ba6768..f2ea6af 100644 --- a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt +++ b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt @@ -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(), 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() } diff --git a/app/src/main/java/com/juhabi/birds/login/model/UserEntity.kt b/app/src/main/java/com/juhabi/birds/login/model/UserEntity.kt index d8c9ce8..1cf6534 100644 --- a/app/src/main/java/com/juhabi/birds/login/model/UserEntity.kt +++ b/app/src/main/java/com/juhabi/birds/login/model/UserEntity.kt @@ -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 diff --git a/app/src/main/java/com/juhabi/birds/main/MainActivity.kt b/app/src/main/java/com/juhabi/birds/main/MainActivity.kt index a31f7ad..3d6d159 100644 --- a/app/src/main/java/com/juhabi/birds/main/MainActivity.kt +++ b/app/src/main/java/com/juhabi/birds/main/MainActivity.kt @@ -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(), 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() { diff --git a/app/src/main/java/com/juhabi/birds/main/fragment/HomeFragment.kt b/app/src/main/java/com/juhabi/birds/main/fragment/HomeFragment.kt deleted file mode 100644 index 2be01ab..0000000 --- a/app/src/main/java/com/juhabi/birds/main/fragment/HomeFragment.kt +++ /dev/null @@ -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(), - 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?) { - } -} diff --git a/app/src/main/java/com/juhabi/birds/main/fragment/MineFragment.kt b/app/src/main/java/com/juhabi/birds/main/fragment/MineFragment.kt deleted file mode 100644 index 4359d79..0000000 --- a/app/src/main/java/com/juhabi/birds/main/fragment/MineFragment.kt +++ /dev/null @@ -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(), - 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)) - } - } -} diff --git a/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt new file mode 100644 index 0000000..89b19aa --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt @@ -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(), + 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) + } +} diff --git a/app/src/main/java/com/juhabi/birds/mine/model/UserInfoEntity.kt b/app/src/main/java/com/juhabi/birds/mine/model/UserInfoEntity.kt new file mode 100644 index 0000000..94fa4fd --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/mine/model/UserInfoEntity.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/juhabi/birds/network/ApiService.kt b/app/src/main/java/com/juhabi/birds/network/ApiService.kt index 2a50e12..b4d88c2 100644 --- a/app/src/main/java/com/juhabi/birds/network/ApiService.kt +++ b/app/src/main/java/com/juhabi/birds/network/ApiService.kt @@ -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, @Body requestBody: RequestBody): Call> + /** + *查询设备列表 + */ + @GET("app/device/list") + fun getBindDeviceList(@HeaderMap headers: Map, @QueryMap params: Map): Call>> - @GET("app/v1/getUserInfo") + + /** + * 绑定设备 + { + "imei": "860000000000007", + "name": "001设备" + } + */ + @POST("app/device/bind") + fun bindDevice(@HeaderMap headers: Map, @Body requestBody: RequestBody): Call> + + /** + * 设备重新命名 + { + "deviceNo": "BM202609160007", + "name": "改名001" + } + } + */ + @PUT("app/device/{deviceNo}/name") + fun deviceRename(@HeaderMap headers: Map, @Path("deviceNo") deviceNo: String, @Body requestBody: RequestBody): Call> + + + /** + * 眼罩控制 + { + "deviceNo": "BM202609160007", + "coverOn": true + } + */ + @PUT("app/device/{deviceNo}/hood") + fun controlEye(@HeaderMap headers: Map, @Path("deviceNo") deviceNo: String, @Body requestBody: RequestBody): Call> + + + /** + * 解绑设备 + */ + @DELETE("app/device/{deviceNo}") + fun unbindDevice(@HeaderMap headers: Map, @Path("deviceNo") deviceNo: String): Call> + + + /** + * 获取个人信息 + */ + @GET("app/user/profile") fun getMineUserInfo(@HeaderMap headers: Map, @QueryMap params: Map): Call> diff --git a/app/src/main/res/layout/bind_device_layout.xml b/app/src/main/res/layout/bind_device_layout.xml new file mode 100644 index 0000000..5c0d0b5 --- /dev/null +++ b/app/src/main/res/layout/bind_device_layout.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/device_item.xml b/app/src/main/res/layout/device_item.xml new file mode 100644 index 0000000..bc63ddf --- /dev/null +++ b/app/src/main/res/layout/device_item.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 7450be1..b04312b 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,8 +1,11 @@ - + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:background="@color/page_background" + android:orientation="vertical"> - + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_no_more.xml b/app/src/main/res/layout/layout_no_more.xml new file mode 100644 index 0000000..d637a24 --- /dev/null +++ b/app/src/main/res/layout/layout_no_more.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_refresh_header.xml b/app/src/main/res/layout/view_refresh_header.xml new file mode 100644 index 0000000..f8763fa --- /dev/null +++ b/app/src/main/res/layout/view_refresh_header.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml index 2ebdc84..f146994 100644 --- a/app/src/main/res/menu/bottom_navigation_menu.xml +++ b/app/src/main/res/menu/bottom_navigation_menu.xml @@ -7,11 +7,13 @@ + android:title="@string/tab_plan" + android:visible="false"/> + android:title="@string/tab_history" + android:visible="false"/> Px#1am@3R0s$N2z&@+hyVZ-^GQTORCt`-oPCTP*LmKb=RGrbm*g%fQld!Fk}c7s zEK4RCTWaE{0V3H>3^-8)eaA+CHg4KFKpUh*P@ri4NPz|^+CQ2Gh+?~6XcET|Wm^dx zx3X)Ms%toL8%vh;EmE>VQj|ne;x4(%z4y+X_vs(!%-nmIyEJ#X+>P=A7kB2~xifQq z^SmE(&Y4TBk=^#}{)w<|awmJ(6%E?~yce!XKocS-AWQ;)OK-vmxG3rnfHy?E5o0_w zhTg%)Z@PbGEzX~-d9aVZ@IUsN@m9Lt>%x!009SJkA`&Qq21o<}Nc6No0g%MzyhRYM zC!IJ1g>U`&O`mvjwc2vY!TsDz-`rWNzDvMfNZSPfWNJ7>mxv4oKLT9ZHl26cPX;Ja zBmm7o+%Nc(e{th4JaH-8b;-be{G~@fW{#h6axWyp0J$g_K(*kKiogp7X8?R2IDlD+ zL;K{DM*XKt1#rc|{oG5BeoU}WN!pGU1ku2A7hA=-C~LrB0A~oCON0_?Euc6_)UaY9 zBCFT|5_%$%|M7b(3~~qc*hL=i`zP{0TAstMA=-&+9AE-W36N$4lS%q`*(1jW_e$4>^tvH!iErAudUA z^T7FVvKRvAP20`^tIt32fxA9`v2DI6aCaU0`jxf9Z$Z9IX?H2sG*>$-CY|uWUFD>P z;@og%Rcbd!V;lzYZz!V$C0A{U;{0|EtKvM0Q_>?x<6Al| z7E)Ui+w$=MKuvha$I8Gz_tc)dKf6>0OIF<7FMjg@b3A}nNy*sM7?3i|1yq-pjmlce z%ZmywZ#9xpThju|t=dMjaJ%fy1!>A4@-zFM{>o>T%3(?1?tSU8UsmA#NcBJtI25w0 z4O})YbFh-Dhk47**Yma;J;4RG%-OUB&?KJ$rgo+E1RKrNjXEXKiC^9G%p?D7DJ(A7 zRqs9c_7ih;ZL;N$lJ z@qV;d*WeW4^Wn`aIWj!QP@M)*($jGJpD}iS@YtR^-@MR^H*B{(dtjoAGoJwQ(*i!! zmLT(DTgs?58rK?V%r)Oox#NZhpZ~x;4_-952VVT<-%_yO@O%PrZ^4Lc;W-C^8bEUo zI#R32<6~9b|H5)%EoL`A_vNdChhGHw^8zL{tbCFSLd)kl?6Pf%B>ao#_ul&_=R4+k z!QFT8TU&$G|3=~lWq0r)c*DwTB2MFqZSe|H#A6lxkH4|&BsJWZLwpW7u+XbKJeQvTsuhJO>9cP9mKE{ z^ORGk&{X?8?u`H8Yr8)F%t}K#pMB^DkK7Ei-vqIvQQL?Lhm@AGY%Y(oYp1&p9eZ)# zeIH%y7>j1rPrdxV?F`{c0$S8xu$17|KYou1XAuBCn=~2KMCSM2n4~J z{Fe*u_Z)D4_lE~=AoxBC3Jgw2DT)OrAcNH20Rb`$;7Rzek6iz;KU)Y;n)?{fjg6?YOTW&x5h!66q{Uxv5;E7yPIYpcX! z$@a{iyZ*=x4`N7VNdc)L-X=F$`W{WM4I}vL^Y=Fb?$fV)`+bDoXM&7$=CX&RTdTHn z)ko;@FYW&5!>dYS8TR}K?s?FOKbarz+}#?LHNbN5Y)O%R&AXr4fA8?VMipoHg*+gT zvZt5mEme)g+7ZaKRq6qaEx-f-s`h4|0i-A3IqDa>>8 zqRTviuoUn7*6rW_!$4|5wB#8JJ139N*|lz>@2_<=aIQNXmYt*~h?Dg^!SQ=J;lwAu4?aR{|*JOQqR$ z<7d9GR*k<{>(2FmHz^DQg#p$8lVk!jG%?Ce(BE!;ZvgOXNB&|rq45BS7Lq%XPMHGf z!Qg?x8#kuW4fsEw-*Lw~#zp7bYajllqXNF{L;_}mgy1>THi~m8-6lcrzVgXGycG>N zkJxKSu1PVwA)RPqT3Qc))8IyW99#E|wQl^yTFvTrhT*tiQ8JfXlq}{L=a5Nmo>ce= zG~meiE&?($50C;<`4O07TUHtKdT_n)2Uq{ZTWj6$i?!GG{nF9s`mO*=;AFC#5SU;! z@y`WQDpmFXK=DtGd}m#R?S#vl4sbA4hRsl3JG91B2E({&lUpvex%^?serj;>$qUdyXs9Wa(+TidE3e3MHlza4zkc*vwKVQSPuBcTpd?(PY>7yGO z%*o@Nd2ANV;5OhG-(z)*JB)NgdIhG;89)z&8{~ud2M^@oe}B!bb8Fq;m9n>g{F6r< zcrHpIBGvp3wXW;J&~h$afW1Xo+h$MtN?njjJ)sCmUB=J~P7+;yaIG7=(pE#?0kki< zDB97mb{tw}oC0Km4#21#p&N2>>(`%m;I;fHYsoD7XU#oPA|_ z96)c)ns*5-PXP-6a0tLIK)21?WO!xg&YduTa|U@JW4fzN@eWF#k&K9NGDMQG93R`b zy3JY@BY^Na5OyV1g`=a23$NnUcKzm?Z>E*-G5tXGIZW0KBjeBYf*x zxAGELI0=NS06UR^T@BcqL-4M}wZrZTlXc!O3y}sV-~pEIQzZ|Q7i6^9C=y;6C)c`_ zD{V)CVrFV8oWiLP0DC(F3xJ(Gc`_gYwG$^!G$2vX<1|IlEZ=dMAWt(GJ@s;iqHs7s zgPg@xYu(V5whgCFb!ImQGi(&5r?F|%CQSjD!4x)K5d=U10Ja7I&~wi{2OAfTo0-$o zD6JtXAoc!Q!Dv>*)CmeF=!Ij;e?IZT-x$?NVy`Sa0W`FwHgo#4ZN_D^X|p^L z)(l(h)v2j){P=M%Ft`3hnCv?X6X0+sU?z(WYrCW*vmnSpGxdNS0A5_{Mz4gu4y+3k zG+{z#XJ!y4(@0*}n7SoTZ!)A|($f$&YV&26;o947i}q`~clV4uqUa)WU!3H@=s}hS zYRnua2k4A)M(tkf2CtM&0PE(?(hRdUN1Dyj&SJ)FhBk{C3p2D?oUz$6Ns$qVX}VjQ zse2+=8U_qz-n^`V1*W87<{_AOtl0K@$Df^8>qcL!NeZXu=GFz$x}Z&L*uXi`47BE* z6B9JhI628#%vc3PG#O%?;gw+K0ZCa({l|`&K>}&;MsmoULEfp053Y5?FV+rYdSj1W z>tW7pj@HGT1>+{o+x;LM>b*R1u+v?DsQ zZoKE42+-JLHBFljkR-Vd>|xG`q{6AGseZGM7TiC&`u%Tt^w$N*qLRDDRM!#aYJj2i z^h6M(2$fxZ_;XUZW)zlal5Dnn#r8hHagbH7ZyZkm*&XKyz-|KAW7im~{_fqo+s=)V z-ZqYfmqO|hmLbi&6hfMT2`Rg9R-~o%!1bHoI{J=D6wjYMxecs3GyT5J^!qkGHpcPv z)G;geY$1RIsflgV!}}Ql_bZ!r9g_QuJdI~3*UZ4oTuL(v4Nwux1tsONx_19te|6)k zQ&^%MJacAibY4b+`HunE@AugUXuZDmd+DD~lxJIILI}8V|MZbvt4d-C_PkDC-bepR>yWek%1E#elAZM4CpkWr+}FsY zHuh+}9y?dsmId1=?Sp zK6XVldv-?+vg(6l9iRe9g8hD0-jMpnF;L`qS#_@?yYu(C@N*8F{C^UpkX074vVvp^ zdD3(mF*HCHOo$PtAlKa9FP;414XXje*YCz>db{aryK5JZeNPW!7u(Hk%Cu?{|f2w0G$ARxjr$QT=NCLlrz8E{G{4nO?X%O{`thqv5PT~acO z*nTP8)IIgCeyq0$Qi)Q1R1l<69Tku&03{&LLXe*5fb#&hWX-UIx%duyr&UdHYxNhD0^7yC{dvz*=OA+ ziHa3K>ywQ;mypk1_S&M4T;f6=^!?(gA6{#&A5V#4pu8rgDGb~-AgK9g0#hn!ECeCe zpp|`v>gd_2@ap~7T(hFn#QyuigJWB_ZhV)@a5?GJ#3*2X|2s39ki79b>rpn$Zk1X)qM zfBZv}SDpA#$G_k|GuZ#u!5b} zD#}7tRiOaXsIIC~j4MhdXhMR9K1!78_bZg0R(Z$wO>V!Sr?{8&hb@2l_A7h)Zo7t+ z79(Iet^svY;*g69L809qNtt!s#=?*l;e|5HAk^t105z-#d8!%|P^hkJghDD%NCku% zkksx+kcy&AYe&OFC`K|x`x^{>nipAf8 zT@xixR~3me&Gf6-CJUk>&;bEG8pfHG~+ zsIEkbMs;Pm9(7f*uE??kDut?!y~E3ahKrfRb>jvQbC#Vuqt2C9nHs3K7ysjlrUObgYFR~shYIC$%(YZkmKxDZ>BKi>P= z>7&=jI$nu#tbgGu5ug1h$NnAX#l~qca3Al)sy&xD2jQ9BO3f#mGJi6sq!jThnFL6=fm!*b5$C zyJh2!lPlTI6$jVYqti#W0N534Y-!XMLZLb*j$t>&klUWCxCG=vz@^_yp)7>hKV53# z#4Q^)t|+j^E*ZGS9-Th2#nssyP`!t+RJda_zKwzl3dQ1zLk)zYXuE|5w^WdYy606f zJ+>*nehYV8(r0NeIk;i_v#F`gp_;wY%Q!iPa2e)-Q^#c;hhs<;FWs%sz=L9y2K z_`0zfp7pmseEH?4SF0_n3U1gANX6{*;mI-<6D-Pc1|KiIo~Wy&D7eOf8VXhwg;kK( z{a#Uz8D>~ocdmDOws`9B?CrPTzN}7yi?#m`<2zx#{SjPx#1am@3R0s$N2z&@+hyVZ-^GQTORCt`-oPCTP*LmKb=RGrbm*g%fQld!Fk}c7s zEK4RCTWaE{0V3H>3^-8)eaA+CHg4KFKpUh*P@ri4NPz|^+CQ2Gh+?~6XcET|Wm^dx zx3X)Ms%toL8%vh;EmE>VQj|ne;x4(%z4y+X_vs(!%-nmIyEJ#X+>P=A7kB2~xifQq z^SmE(&Y4TBk=^#}{)w<|awmJ(6%E?~yce!XKocS-AWQ;)OK-vmxG3rnfHy?E5o0_w zhTg%)Z@PbGEzX~-d9aVZ@IUsN@m9Lt>%x!009SJkA`&Qq21o<}Nc6No0g%MzyhRYM zC!IJ1g>U`&O`mvjwc2vY!TsDz-`rWNzDvMfNZSPfWNJ7>mxv4oKLT9ZHl26cPX;Ja zBmm7o+%Nc(e{th4JaH-8b;-be{G~@fW{#h6axWyp0J$g_K(*kKiogp7X8?R2IDlD+ zL;K{DM*XKt1#rc|{oG5BeoU}WN!pGU1ku2A7hA=-C~LrB0A~oCON0_?Euc6_)UaY9 zBCFT|5_%$%|M7b(3~~qc*hL=i`zP{0TAstMA=-&+9AE-W36N$4lS%q`*(1jW_e$4>^tvH!iErAudUA z^T7FVvKRvAP20`^tIt32fxA9`v2DI6aCaU0`jxf9Z$Z9IX?H2sG*>$-CY|uWUFD>P z;@og%Rcbd!V;lzYZz!V$C0A{U;{0|EtKvM0Q_>?x<6Al| z7E)Ui+w$=MKuvha$I8Gz_tc)dKf6>0OIF<7FMjg@b3A}nNy*sM7?3i|1yq-pjmlce z%ZmywZ#9xpThju|t=dMjaJ%fy1!>A4@-zFM{>o>T%3(?1?tSU8UsmA#NcBJtI25w0 z4O})YbFh-Dhk47**Yma;J;4RG%-OUB&?KJ$rgo+E1RKrNjXEXKiC^9G%p?D7DJ(A7 zRqs9c_7ih;ZL;N$lJ z@qV;d*WeW4^Wn`aIWj!QP@M)*($jGJpD}iS@YtR^-@MR^H*B{(dtjoAGoJwQ(*i!! zmLT(DTgs?58rK?V%r)Oox#NZhpZ~x;4_-952VVT<-%_yO@O%PrZ^4Lc;W-C^8bEUo zI#R32<6~9b|H5)%EoL`A_vNdChhGHw^8zL{tbCFSLd)kl?6Pf%B>ao#_ul&_=R4+k z!QFT8TU&$G|3=~lWq0r)c*DwTB2MFqZSe|H#A6lxkH4|&BsJWZLwpW7u+XbKJeQvTsuhJO>9cP9mKE{ z^ORGk&{X?8?u`H8Yr8)F%t}K#pMB^DkK7Ei-vqIvQQL?Lhm@AGY%Y(oYp1&p9eZ)# zeIH%y7>j1rPrdxV?F`{c0$S8xu$17|KYou1XAuBCn=~2KMCSM2n4~J z{Fe*u_Z)D4_lE~=AoxBC3Jgw2DT)OrAcNH20Rb`$;7Rzek6iz;KU)Y;n)?{fjg6?YOTW&x5h!66q{Uxv5;E7yPIYpcX! z$@a{iyZ*=x4`N7VNdc)L-X=F$`W{WM4I}vL^Y=Fb?$fV)`+bDoXM&7$=CX&RTdTHn z)ko;@FYW&5!>dYS8TR}K?s?FOKbarz+}#?LHNbN5Y)O%R&AXr4fA8?VMipoHg*+gT zvZt5mEme)g+7ZaKRq6qaEx-f-s`h4|0i-A3IqDa>>8 zqRTviuoUn7*6rW_!$4|5wB#8JJ139N*|lz>@2_<=aIQNXmYt*~h?Dg^!SQ=J;lwAu4?aR{|*JOQqR$ z<7d9GR*k<{>(2FmHz^DQg#p$8lVk!jG%?Ce(BE!;ZvgOXNB&|rq45BS7Lq%XPMHGf z!Qg?x8#kuW4fsEw-*Lw~#zp7bYajllqXNF{L;_}mgy1>THi~m8-6lcrzVgXGycG>N zkJxKSu1PVwA)RPqT3Qc))8IyW99#E|wQl^yTFvTrhT*tiQ8JfXlq}{L=a5Nmo>ce= zG~meiE&?($50C;<`4O07TUHtKdT_n)2Uq{ZTWj6$i?!GG{nF9s`mO*=;AFC#5SU;! z@y`WQDpmFXK=DtGd}m#R?S#vl4sbA4hRsl3JG91B2E({&lUpvex%^?serj;>$qUdyXs9Wa(+TidE3e3MHlza4zkc*vwKVQSPuBcTpd?(PY>7yGO z%*o@Nd2ANV;5OhG-(z)*JB)NgdIhG;89)z&8{~ud2M^@oe}B!bb8Fq;m9n>g{F6r< zcrHpIBGvp3wXW;J&~h$afW1Xo+h$MtN?njjJ)sCmUB=J~P7+;yaIG7=(pE#?0kki< zDB97mb{tw}oC0Km4#21#p&N2>>(`%m;I;fHYsoD7XU#oPA|_ z96)c)ns*5-PXP-6a0tLIK)21?WO!xg&YduTa|U@JW4fzN@eWF#k&K9NGDMQG93R`b zy3JY@BY^Na5OyV1g`=a23$NnUcKzm?Z>E*-G5tXGIZW0KBjeBYf*x zxAGELI0=NS06UR^T@BcqL-4M}wZrZTlXc!O3y}sV-~pEIQzZ|Q7i6^9C=y;6C)c`_ zD{V)CVrFV8oWiLP0DC(F3xJ(Gc`_gYwG$^!G$2vX<1|IlEZ=dMAWt(GJ@s;iqHs7s zgPg@xYu(V5whgCFb!ImQGi(&5r?F|%CQSjD!4x)K5d=U10Ja7I&~wi{2OAfTo0-$o zD6JtXAoc!Q!Dv>*)CmeF=!Ij;e?IZT-x$?NVy`Sa0W`FwHgo#4ZN_D^X|p^L z)(l(h)v2j){P=M%Ft`3hnCv?X6X0+sU?z(WYrCW*vmnSpGxdNS0A5_{Mz4gu4y+3k zG+{z#XJ!y4(@0*}n7SoTZ!)A|($f$&YV&26;o947i}q`~clV4uqUa)WU!3H@=s}hS zYRnua2k4A)M(tkf2CtM&0PE(?(hRdUN1Dyj&SJ)FhBk{C3p2D?oUz$6Ns$qVX}VjQ zse2+=8U_qz-n^`V1*W87<{_AOtl0K@$Df^8>qcL!NeZXu=GFz$x}Z&L*uXi`47BE* z6B9JhI628#%vc3PG#O%?;gw+K0ZCa({l|`&K>}&;MsmoULEfp053Y5?FV+rYdSj1W z>tW7pj@HGT1>+{o+x;LM>b*R1u+v?DsQ zZoKE42+-JLHBFljkR-Vd>|xG`q{6AGseZGM7TiC&`u%Tt^w$N*qLRDDRM!#aYJj2i z^h6M(2$fxZ_;XUZW)zlal5Dnn#r8hHagbH7ZyZkm*&XKyz-|KAW7im~{_fqo+s=)V z-ZqYfmqO|hmLbi&6hfMT2`Rg9R-~o%!1bHoI{J=D6wjYMxecs3GyT5J^!qkGHpcPv z)G;geY$1RIsflgV!}}Ql_bZ!r9g_QuJdI~3*UZ4oTuL(v4Nwux1tsONx_19te|6)k zQ&^%MJacAibY4b+`HunE@AugUXuZDmd+DD~lxJIILI}8V|MZbvt4d-C_PkDC-bepR>yWek%1E#elAZM4CpkWr+}FsY zHuh+}9y?dsmId1=?Sp zK6XVldv-?+vg(6l9iRe9g8hD0-jMpnF;L`qS#_@?yYu(C@N*8F{C^UpkX074vVvp^ zdD3(mF*HCHOo$PtAlKa9FP;414XXje*YCz>db{aryK5JZeNPW!7u(Hk%Cu?{|f2w0G$ARxjr$QT=NCLlrz8E{G{4nO?X%O{`thqv5PT~acO z*nTP8)IIgCeyq0$Qi)Q1R1l<69Tku&03{&LLXe*5fb#&hWX-UIx%duyr&UdHYxNhD0^7yC{dvz*=OA+ ziHa3K>ywQ;mypk1_S&M4T;f6=^!?(gA6{#&A5V#4pu8rgDGb~-AgK9g0#hn!ECeCe zpp|`v>gd_2@ap~7T(hFn#QyuigJWB_ZhV)@a5?GJ#3*2X|2s39ki79b>rpn$Zk1X)qM zfBZv}SDpA#$G_k|GuZ#u!5b} zD#}7tRiOaXsIIC~j4MhdXhMR9K1!78_bZg0R(Z$wO>V!Sr?{8&hb@2l_A7h)Zo7t+ z79(Iet^svY;*g69L809qNtt!s#=?*l;e|5HAk^t105z-#d8!%|P^hkJghDD%NCku% zkksx+kcy&AYe&OFC`K|x`x^{>nipAf8 zT@xixR~3me&Gf6-CJUk>&;bEG8pfHG~+ zsIEkbMs;Pm9(7f*uE??kDut?!y~E3ahKrfRb>jvQbC#Vuqt2C9nHs3K7ysjlrUObgYFR~shYIC$%(YZkmKxDZ>BKi>P= z>7&=jI$nu#tbgGu5ug1h$NnAX#l~qca3Al)sy&xD2jQ9BO3f#mGJi6sq!jThnFL6=fm!*b5$C zyJh2!lPlTI6$jVYqti#W0N534Y-!XMLZLb*j$t>&klUWCxCG=vz@^_yp)7>hKV53# z#4Q^)t|+j^E*ZGS9-Th2#nssyP`!t+RJda_zKwzl3dQ1zLk)zYXuE|5w^WdYy606f zJ+>*nehYV8(r0NeIk;i_v#F`gp_;wY%Q!iPa2e)-Q^#c;hhs<;FWs%sz=L9y2K z_`0zfp7pmseEH?4SF0_n3U1gANX6{*;mI-<6D-Pc1|KiIo~Wy&D7eOf8VXhwg;kK( z{a#Uz8D>~ocdmDOws`9B?CrPTzN}7yi?#m`<2zx#{SjPx#1am@3R0s$N2z&@+hyVZ}07*naRCt{2eQS^%$93L!PS1TTb{D{ccn}~c10+F8 zfIx}_%aLd)7NUn~sYHsDKt19pwu*|%<&-K)ek7H|k>ph6X**R)Dpn|&lw^^% z9<(Kap=3p-`4B__d@M*%BnT1@F0k0=y?3U+{OInPo_l8(i^T%FAaM$dy*tx)W^RA; zo%5YD-F*e;xxIPsodc5eT@EW3L(SzNF9taP08qUc0BE@$00@keoN)Uv4qzORanc^9 z$Z(dV`x~v+i}!82ZG2Hb)ESyA_63}-z2))mUacb6Sl}7~F1NIU#m8X&c`PXq^_%^6 z39rTPkJiuot{=eJy&KDPLv`e!BIh zhtF+q&1_2o)OSAly|-r2e<Vz5#R*~|JMUsZ~pdK?YvoRQ9%98;lF;p5+4D1u|=6-jsc-#+2w8( ztca_2A(*NFY!F6~Nb;aDzD7hq=is{b)}2s5ykJ#6_0S?P_nd-y%l>=Uwy7V4K%ttc3%v5CeO9*c!-Jur&r1RdIDTrUvg2-9VB1ojIPysyBEMBF}Tdz|`V~ zA-a^i4;uu=MD4$L_%*jYwBQ{ce~`0bH$8Fh`=R>&TxBv+u<)WM04TGuwO~Zymdh@< zN�g6<+yrY3iW-*&$hq&4;Q-MaW+7`W&gcvEt7vm z*vo(@?v#Lapdxa^Gso5{7u*|HyM>AaRU|&%j7*&|o}wg(N-RZi=jggAW)2tVN`*w^ z3AE&&KXTp8hZYh8UC4vovj5(-wC3LnxLJ^E9gia*lHd3Y;y-M2zv*vvZ_MAw@|i5U zEo4EbY8P)Qzk%gbla6;Kb=+lfC98XvW$!+gw~@RmtJ}|C`|#ZxW<19{Y{5XiWzW59 zVa*?iVhy|t&;UjRiUjx21(d`W69HY-qwzM;ZouOx~zsGj{9lehKwm}>fKvp;z3SbLB;{Z`+@FiKA z-Ffw%yRYi~B&Tm@4C=cN?B1jqKLv}m2&}OMcp5WFBnQ^t?v*Ud+j_QlGrNj~-Ha~& zF1L&S$~)EuH(D+YmM;f|pQ&W2e{ZDd9ZR)nm%Ck*a#<(Xu$-EHaUp;`BcZfw`2IB+ z+J7iOKimTjDV|BXM_HkRtQR!pT50IKB6qD)!bJtAOI1esDwTU+F<})?fp`j%jWgB5 z0C1mw{`uy;cMbsk0|3xMI$%t41v0yy;CfMW1ud^E<)_`Mw@}yxOC>6NR8+ofmq_b5 zZV3kg1&2C`e)x%3&tE*v8`NFH_pc$+PXb(9)m__+mtVpi%?e|7P1Ow9MQ1bg$|I|o{5Y=P+C2XHNbD^Zo3cIBm^cHErdNI!4^SSoZu{Z3!`&x)xN8o)VmsjSck@v{^A?mb2nV+UZr##TU2cxwXom6_sSc;KcDMe2z&j$d#+fc1^t7g$p%Q=A5}-xJ zjh}h!>i2%;bguEVK;1R`{jDnc?{W_m*KQ+Vi<)N_V`JeI7wo&@D`Afa<&M9+>g_)` zo$EW7wqxHt*9rLvl9!;{?e-|$_=(M_7^-;*GwieEKYj9=U32b>IfJ@u`2ICUv;Rms zrCV}8(pwlubO%?(*qP$11-|;7E8l)j9#NgCUG?L;2m4pr4*>E{AQVu#80sJRcdKbs z#Mj_*LuoSxn>6F+|qdAOx}x>NwlF7-nW|G`zXe5~D~dWdDx$?B4DWv7 zi?)J-wXmV^TQFAW8S9Srg_jec0I+A6WC>d(DB1-Y@*c~W(j26e)t{5vS0oVB3)+r?TJ21;?Zi?K@i(zmX9%L5D7jZ4pdzU}6Ftd-xNiJ`rN2Bepr zK0pQG1o7ok#lM~H+%tmukDmI`8=<(!DOTr7z1xLCoZI#LM3ezs6`=-6C;lez0!pE@ zv|s-EmYw%4crPwN+k4HeKLq$+N`lBes`4y!jtdNI@Y^-$;{s>^5{>S-^s+sFHe;n} zMo?9FvzJ{C7!~>=rUQ}wlryl_AI8U7=Z+oFwJUkv-j_V@fLfj=Ro2 zlU=dX#o-WR9hf2iCEUIY_-8Ynvl7$~KmFsYROBLMV3-G2%HXOf6+Vseu{N+)V|NS! zMjp89vYlT)i~YA`?XlOs=TAs{tE_GXC3ZUZ>RemoY70&21XnR5fS0~%&zo@TNDz44^ij*g>)>H!W}!-9+_$m{?D`6Z%f`Rv(Jz`1kkBr&$tjf zsZn(EJiE(8-Nhde!S{jJ)nO0=^tMuqE{BN)Pbui49qo}SNeQ0!9u-D)?7#x ztToC%`il+MPo9ImTe9}#>u!6Vky4M19#amEaAvCb|8+TmxmS3K;-dn=vf&CvjJGiYaT9I=)=bQVpkQUrVxK1lCCt_h@$t*<*O+Tu=}+l- z=zvuRu0kj|dn0C|m$@Jw`|{?UkDu!fJ0I-n?Yq87$lvCQ)&mBLcd;H!6kG{FA_CJ* zBcZ^``{Lv0p#I)t-@gcyEduqikiNDH3Y98i9diSFXJ@WS?Y9=C$Icf^vrp&jvs5b( z3#*QaR%c=*>43CwY$hR8-mvO{uU;AsorB83fvZ(W71D@Ufl^dMp2-q}J$sF+RS4CH z^+gHmWA0;LzUl%?XR~K^y#J@9{B00F4!kZ_657p5OIJ1s<+`Ar6Jvif9J_G8nY~(a zw)x@#=^5&_72t|>&K9g@uim{_opoLr?KjHtvWt}wkXZy|C{V=jwTd#RAAjyAeF9t( z+DmY|P%1*zsGGrMfK|l0*o%oTUiQW(7PYg^6MOdB_uoa}c|wFaE3FppRk|2i5=>fD znV=xHhvNpOULugDOK|Wj4kj=4@75#8oX^GK3z~1n$w@1|zW1 zSQr3?pcMen~#Z7SxXLR}!JDH{yrR z0AQF8HYz9w}1x2`V|W2S{ok!QPcgNZ&f;f&`5Wz{ z8N;%tQp|XeVsNc%m(QL<5o?KJ>=K3&g*2kkf1Z)ijbbUt{JyT`ZD_5yD6SB4K^J&> zrI|%GN+$Unv)tm9)X+6iCRz|IEMh}Y5)so7^()cXUW!+)Q=}fyVxkmkEA)a$Is=86C%Ey9U>zRS#`fpZaMqqgDDC(i6cT`<6_+XAZ4!&Pvy zaw`a`8numhsEQDj%>OX@?ZzG1nSOv1(`W6|z7p)Z+ka{E;U--_D1L1CEp9ZT6^f01 zQ@ANHV(M{2dFKUC&(JLJ2Xkpx$RqY_0f0mzku;M@tavZjSueN zDoY3)H=-+MKU?%V%mc^;l-uyh1G`s#a`Vm$k@d{%w3ZdT3wHr`mACEMwTp>?awfKl zDtjEW#`|450{~4sc!AAlV7|C8E2s}W^bmIJ*pc`1F6;t!^)wST!sB72gQ{9Z@#DrH zhLI3r-4ERl=te+LtWqrcFgx2X*j!AzfA;U+uc2tWW5*7O_w568-@bjiZ{NPW4O~n$ zba`r-l5Uc5WO+tfPNm>oG25nb_CNE>+B9bBK>#>-5LaAr1>@d+=9y;z0Gl>#;#L6I zx;5+yZEPa2)5w$-fKjE*my8Kb%JH*EoeI!t0i($p|Gj4yY<^|{xMfdq7#p#U*tih@ zj}oU9*PA0Ku_i z0DyI?*YW5&;ONn#0sw2)tl@e9hu5zMa2NoXXr`l7Unb1KbE7_E@Bl!N5E{i^w+5CQ z2W*oKpB^4=d~C~>*17GA^WR9yC`M~T7$N{!1y^-Fv1%1o;5cy{;Of-`fRT|AF#uLM zuyO3zF#&*et5*|P2LM)Ulg%R%M&d_hLN_F|P`_e9*bWF%)K{idEL1Hm2vu8aPF!%o z^RS2TP%=KIdJ@osCr|1qAc9dD8Xc7(49U>YkSL%h0X?}&RaS`{KYm;l5M8}mR8>Ys zMuGySer(H@mYQU$1fX96KtUQ6YarIB*d(cEMqR2CQXt7dqhb}LHMPx)-9hK0?ZA$@ z4niiz#&pH<Iff2I1t`8fR4lEFvp~jn8kQr!U~{pxIMxsmnVOtZ0c2`oN@YM~YGOkD;<9pN zd|YKv9GHOUAaan$mP-yYfCALtz4Ud@!)7f7sYYltiP)G}H}fql6^H$J(@oZ2N@U{rnEuD5+p$aO3fm-Ya%zS+W(~^PiIMU+Y9m zMMPT1r*#@r(wcIybsD*cm8nU`-s#CH?PjoGQUoG*0qt1*vLijBM}uM6YloshmEsPMQ#rmT0RbGTjt` z16P`m_Vlz$-rUgPE^9oHNsb>BlWb zf{N~6|5rwyx&Uls2XS&`O|;f-wWSS+pe5}H+}3ncnk#`8Afth~(<0NJztfm1vG>Hd zO^ZSG&U(hg*mgQB#<3Dv1~uxLiGrD0FjLioX_N{>H7@QVvk;?1mTmvvFYf6-r#-hs zZJ2C~kF{5~0cir#gh)G&p=EF@nH#g$yJidwoa{bN52&|q*!oL@Y%H*@U?$4U)R>vZ zr$TfMOstd*lltI4c%=hNQrQ~4qzy>hgV_dTd86UJA-CCZbJv9luuV?N+ zU%j<96OCAv|GgEVBnjB1j00J3=N}E|X%q0WVxVWr_6w0z@GPvzrbly_6 z{Zk_w*iP$>dIQpK3F-~B+ujAYS|P;dakJsUZK8pKxsKi~A6KWIIo88c%=TqNUpzAW zUZb`@vw*5$81^+Npw84zdNH?)$OMI)N70Xt334q28zy(*Tljde1n{ z)S~^fz)T>6V_ocO{WK2*c4n1;=JD2mHJGhme}$D0A3J&SB1LfYtn8)!>g&}cwft;i)Kd}Ex8w-IyOeSI?yoSzZY+t+T}ubCb1j+;ORdRLprP6NN+ z3n&%>3UKVrGB-%Z8oB1HN1xqrwmNYs*#UB8TPD}n0Cdn6ZMUTZk#@T+B9L~w?SO8# zqz(6Xh_%vci9n>?YUkiK;9PQzOt*=Q&vf2dM}((|{+(;&*ea?dU11`Qoq?UmGQ`YA zaMM}gt`*9f;f{Nz4!vsOyK(8*Fkf0e*&f*d)R97@hB`VP81)M{IMvgZ9@e%!Ya=05 zq(G2%U*FN$&V9-+@Vn!s;Wtq9GMo8fp#iKMOl210Z4^Hpu!b4RfI6nT)JH{se#^>N zJ+)9Bxm4`IlP6b;PHm{6MxYI{PN0nhHFf~he1BN)$?A2m)dK4EI?--{^#;*)qPAYI z6D=2O(E^MyJ}IabO1r!Gqw)2+dj^=9cx1z?PjfQ&~EDQ zKX$5XnA0~M|LM`EuM%)a022Uc$ef+AyY+2o9BWa*Q21}$1^~iPR9-kTeE5-%UVnY( zjCyntZ7*5J^zn<^S-Psz0g@V7Lyc{8P-ZNU)X7d8sMSHVffj&T=#QX|D_GkcOpa`p zp;OK*ob%UL-|^Cc8)5BGaIM)K09Bvn04~;MQT`fIBpT4bkiyCOih&2-As1EVUQ5~b zlJ(B`$d*nct0(Omfez?_bifuamo*1uQYX;{O77TitJmvnwc0tb0AL3DC$>m+3e#bPT`C9g&+zFw*WLlU}zSlJmIEb#C&7XmmMGf^>3d%c;#mwe6Tj3 z&Rq<9aN_9tsmWunP1QDP08;7rHs?M+dFLmfV3@=`fVM!ZQ6DMs@-mv z+;LK`XQL18J3QBGnln=Y;Lc;uU6M)a*OV>hw1$*Ls~AcjTT3e0vl$r-41p9%Q>+@J zoJ=*@bL;YrOLlFh$9VFhiHUTBh}O~RI0nMG-A)Hc7*@fF85>-2!Pt_3^6uD146PF! z8*e{*{e}&5{wkQ$0u@dTZ!<8yfCG2!MK-&R}c``Pundz&WE1y2yYn>OUf5Ah` zCXct@Oxpe`hLQo~GDD4VfQHvdQ>dwCDG{?d1C60lIz;W6!4-{>H_6K5^SSoZvF~uW zv0`-jMq#>K)YRQ|x$ASlc2H((rvR}=x1A2?poq}{YPB+B+W=}^xoq8L?aAe9p1EG+ zv@?QvfeH`o^zrtuleWL)a(fsWD_s|Jjj4#EKqX5<)vE9aZq4v4vty{qNM`$w^siVk zwo@dX?`H|Nd-v|7@6A^)BXm`IqP;>5mYKu>!CFFe(wv1!;+QrYutj|B0v70DY#VJ4 zEKq0Go)}zxU_OB2Oh82htT0=K9MG7d<LZ%(S%#p2SHJq@64xDf*=dkEvtf?_uR$uk>W|1>mXzaAs?WwZg*?jE@i}Ze* z+D@K#da)EbwghNesNHTr0U!&=C0|)?QsYp_8 zgjy}Ck0donno0p?&F^<@(vC>2777zJ)S}`9YCx@)v>$)&@PYt}g#r}--nC)FWTVyi zk&2xFfGn^jDBUQIX2>b&#ZTdOyDg8U01Ta*EZWVijb>(Q@uHix(`n`zn9dTG9K31A zgR_{ISpWb807*naR9L~*f~Cd2v?C?Z!+zFG+w6bt>D#t#Yj+=KzP4bX!ULPMnm^W5 zMm1&~Q0n0lOS?L@Xg{;frA^}hGfBB^61_;esGM{$yB0Ay6UWx9lL~5y-|xXmI})?i zZFX!;JB4nkp$60o#x{&hHTNHWYTE(=iiHqLGubyso_(zmT;~2Ngi)@JD_L50y>S|o zt6rped=6?f1)xDB1z{D7r11VEVd{ks0MIkviJ@4K6=JCqy*8v%!8H(Gn^xwn?v2%I z*ehoeFZ}Aj+F75A&es-{sj=O<>Q%qUjQ)+K?F2v($5gLe3(IC@pfSme`|2?pOF_6d zA}3X^liocco|dfUT4gc&Y87lnA(m>j8YFJtGHqFwJbktR#n}WE0B&8r@ug(?*#FJQ zm$I&$G)P)@$I^(Us8GGAh^M94Sc=QNLArDsWwXW3UdvJ`+IyW8lyGd)9g{u4^`tRb z8(1}by{sBNo%5cqoi+E{ZTCz)vsv4ErOjB2Q<(0&w9@q=%|#O9ZQd3dBq`kt;7MNeSZ{rb;Lw5;wg}EuW*r z*64hNU~&~EpqH(NSxfut&t5x+;U^1iivnt}Z@e_Tv0-alv~HTA3KiU@CNrzv*!8AJ zeNA|dpw8faovSXF3*m|tl`c5->Nnh60aRnOWo-8R^~*P$a)8*`vPA_o*!M=CTa|UN z#ndo_o`M4^7*uqo^=v*JrpCa!V!e;=KCUK-VaHc5DpQ*zAv`O?(;I8F)v(Qj*Dqhb zXkZ3g5}*eA-sp3yIvre*Br*umB(OA8k&1e6m+OURTB(o^mnK!@sX*$B{MAf#x$MmO zDk2wL6Eo7bwWDuZdG??0&efI_sKIuV$>fOxm#FrwHRMnasF5oygPIgidf-Zt^Ih+f6pp=@-_rm9DkaL6Iym5o1cAcBL~_Obs>P zusyFT%?4`H=-8|?VKo^UUv^^T9kTR*jBUw-8ryC%nHYU`u!-hShHS_(8#FaB$cz0h zAfFCXs}!)%rlJ^6O>MN%htb!cXJe7iD^RO!H<密码 登录 手机注册 + 绑定设备 请输入账号 请输入密码 显示密码 @@ -18,6 +19,7 @@ 没有账号?立即注册 获取验证码 %1$d秒后重新获取 + 已经到底了 @@ -63,6 +65,8 @@ 历史 我的 首页 + 重命名 + 解绑设备 计划 历史 我的 diff --git a/gradle.properties b/gradle.properties index d621849..837592e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,3 +14,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.useAndroidX=true +android.enableJetifier=true