设备相关

This commit is contained in:
fengjignqi
2026-09-19 08:41:53 +08:00
parent f7cf112dc9
commit 981cfae71c
26 changed files with 839 additions and 72 deletions

View File

@@ -66,4 +66,8 @@ dependencies {
androidTestImplementation libs.androidx.junit androidTestImplementation libs.androidx.junit
//屏幕适配 //屏幕适配
implementation 'me.jessyan:autosize:1.2.1' 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'
} }

View File

@@ -65,6 +65,10 @@
android:name=".login.ForgetPsdActivity" android:name=".login.ForgetPsdActivity"
android:exported="false" /> android:exported="false" />
<activity
android:name=".device.BindDeviceActivity"
android:exported="false" />
<provider <provider

View File

@@ -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();
}
}
}

View File

@@ -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()
}
}

View File

@@ -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) {
}
}

View 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
}
}

View File

@@ -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

View File

@@ -15,6 +15,7 @@ import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.main.MainActivity
import com.juhabi.birds.mine.MineCenterActivity import com.juhabi.birds.mine.MineCenterActivity
import com.juhabi.birds.network.TokenManager import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil import com.juhabi.birds.util.SharedPreferencesUtil
@@ -191,7 +192,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN) SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN)
ToastUtil.showShort(mContext, "登录成功") ToastUtil.showShort(mContext, "登录成功")
startActivity(Intent(this, MineCenterActivity::class.java)) startActivity(Intent(this, MainActivity::class.java))
finish() finish()
} }

View File

@@ -1,11 +1,8 @@
package com.juhabi.birds.login.model package com.juhabi.birds.login.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.io.Serializable
data class UserEntity( data class UserEntity(
val scope: Any? = null, val token: String
val openid: Any? = null, ) : Serializable
@SerializedName("access_token")
val token: String = "",
)

View File

@@ -10,8 +10,8 @@ import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActivityMainBinding import com.juhabi.birds.databinding.ActivityMainBinding
import com.juhabi.birds.main.adapter.MainPagerAdapter import com.juhabi.birds.main.adapter.MainPagerAdapter
import com.juhabi.birds.main.fragment.HistoryFragment import com.juhabi.birds.main.fragment.HistoryFragment
import com.juhabi.birds.main.fragment.HomeFragment import com.juhabi.birds.device.IndexFragment
import com.juhabi.birds.main.fragment.MineFragment import com.juhabi.birds.mine.MineFragment
import com.juhabi.birds.main.fragment.PlanFragment import com.juhabi.birds.main.fragment.PlanFragment
import com.juhabi.birds.main.model.AppVersionInfo import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.main.mvp.MainContract import com.juhabi.birds.main.mvp.MainContract
@@ -42,7 +42,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
override fun initView(savedInstanceState: Bundle?) { override fun initView(savedInstanceState: Bundle?) {
binding.viewPager.adapter = MainPagerAdapter( binding.viewPager.adapter = MainPagerAdapter(
this, this,
listOf(HomeFragment(), PlanFragment(), HistoryFragment(), MineFragment()) listOf(IndexFragment(), PlanFragment(), HistoryFragment(), MineFragment())
) )
binding.viewPager.offscreenPageLimit = 1 binding.viewPager.offscreenPageLimit = 1
binding.viewPager.registerOnPageChangeCallback(object : androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback() { binding.viewPager.registerOnPageChangeCallback(object : androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback() {

View File

@@ -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?) {
}
}

View File

@@ -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))
}
}
}

View 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)
}
}

View File

@@ -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

View File

@@ -1,5 +1,6 @@
package com.juhabi.birds.network 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.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.main.model.AppVersionInfo import com.juhabi.birds.main.model.AppVersionInfo
@@ -7,10 +8,13 @@ import com.juhabi.birds.mine.model.MineUserInfo
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.HeaderMap import retrofit2.http.HeaderMap
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.QueryMap import retrofit2.http.QueryMap
import retrofit2.http.POST 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>> 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>> fun getMineUserInfo(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<MineUserInfo>>

View 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>

View 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>

View File

@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?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_width="match_parent"
android:layout_height="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 <RelativeLayout
android:id="@+id/rl_title" android:id="@+id/rl_title"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -12,10 +15,39 @@
layout="@layout/include_title_home_layout" /> layout="@layout/include_title_home_layout" />
</RelativeLayout> </RelativeLayout>
<TextView <TextView
android:id="@+id/tv_index"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/home_page_title" android:text="@string/home_page_title"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="24sp" /> 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>

View 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>

View 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>

View File

@@ -7,11 +7,13 @@
<item <item
android:id="@+id/navigation_plan" android:id="@+id/navigation_plan"
android:icon="@drawable/ic_nav_plan" android:icon="@drawable/ic_nav_plan"
android:title="@string/tab_plan" /> android:title="@string/tab_plan"
android:visible="false"/>
<item <item
android:id="@+id/navigation_history" android:id="@+id/navigation_history"
android:icon="@drawable/ic_nav_history" android:icon="@drawable/ic_nav_history"
android:title="@string/tab_history" /> android:title="@string/tab_history"
android:visible="false"/>
<item <item
android:id="@+id/navigation_mine" android:id="@+id/navigation_mine"
android:icon="@drawable/ic_nav_mine" android:icon="@drawable/ic_nav_mine"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -7,6 +7,7 @@
<string name="login_password">密码</string> <string name="login_password">密码</string>
<string name="login_button">登录</string> <string name="login_button">登录</string>
<string name="login_register">手机注册</string> <string name="login_register">手机注册</string>
<string name="bind_device">绑定设备</string>
<string name="register_account_hint">请输入账号</string> <string name="register_account_hint">请输入账号</string>
<string name="register_password_hint">请输入密码</string> <string name="register_password_hint">请输入密码</string>
<string name="register_show_password">显示密码</string> <string name="register_show_password">显示密码</string>
@@ -18,6 +19,7 @@
<string name="register_prompt">没有账号?立即注册</string> <string name="register_prompt">没有账号?立即注册</string>
<string name="register_get_code">获取验证码</string> <string name="register_get_code">获取验证码</string>
<string name="register_code_countdown">%1$d秒后重新获取</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_history">历史</string>
<string name="tab_mine">我的</string> <string name="tab_mine">我的</string>
<string name="home_page_title">首页</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="plan_page_title">计划</string>
<string name="history_page_title">历史</string> <string name="history_page_title">历史</string>
<string name="mine_page_title">我的</string> <string name="mine_page_title">我的</string>

View File

@@ -14,3 +14,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true