设备api和圆环进度条

65 阅读11分钟

为你设计这个设备固件升级界面,包含圆形进度条和倒计时功能:


package com.tplink.deco.activity;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.tplink.apps.architecture.BaseMvvmActivity;
import com.tplink.deco.databinding.ActivityDeviceListBinding;
import com.tplink.deco.model.DeviceListResponse;
import com.tplink.deco.viewmodel.DeviceListViewModel;
import java.util.ArrayList;
import java.util.List;

public class DeviceListActivity extends BaseMvvmActivity<ActivityDeviceListBinding> {
    
    private DeviceListViewModel deviceListViewModel;
    private DeviceListAdapter deviceListAdapter;
    private String userToken;
    
    @Nullable
    @Override
    protected ActivityDeviceListBinding bindContentView(@Nullable Bundle bundle) {
        ActivityDeviceListBinding mBinding = ActivityDeviceListBinding.inflate(getLayoutInflater());
        return mBinding;
    }
    
    @Override
    protected void subscribeViewModel(@Nullable Bundle bundle) {
        // 获取从登录页面传递过来的token
        userToken = getIntent().getStringExtra("user_token");
        
        // 初始化 ViewModel
        deviceListViewModel = new ViewModelProvider(this).get(DeviceListViewModel.class);
        
        setupViews();
        observeViewModel();
        
        // 自动加载设备列表
        if (userToken != null) {
            deviceListViewModel.refreshDeviceList(userToken);
        }
    }
    
    /**
     * 设置UI组件
     */
    private void setupViews() {
        // 设置RecyclerView
        viewBinding.recyclerViewDevices.setLayoutManager(new LinearLayoutManager(this));
        deviceListAdapter = new DeviceListAdapter();
        viewBinding.recyclerViewDevices.setAdapter(deviceListAdapter);
        
        // 设置刷新按钮(可选)
        viewBinding.btnRefresh.setOnClickListener(v -> {
            if (userToken != null) {
                deviceListViewModel.refreshDeviceList(userToken);
            }
        });
        
        // 设置返回按钮
        viewBinding.toolbar.setOnClickListener(v -> {
            finish();
        });
    }
    
    /**
     * 观察 ViewModel 中的 LiveData
     */
    private void observeViewModel() {
        // 观察设备列表
        deviceListViewModel.deviceList.observe(this, deviceList -> {
            if (deviceList != null) {
                deviceListAdapter.updateDeviceList(deviceList);
                
                // 更新空状态显示
                if (deviceList.isEmpty()) {
                    viewBinding.textViewEmptyState.setVisibility(View.VISIBLE);
                    viewBinding.recyclerViewDevices.setVisibility(View.GONE);
                } else {
                    viewBinding.textViewEmptyState.setVisibility(View.GONE);
                    viewBinding.recyclerViewDevices.setVisibility(View.VISIBLE);
                }
            }
        });
        
        // 观察设备总数
        deviceListViewModel.totalDeviceCount.observe(this, totalCount -> {
            if (totalCount != null) {
                viewBinding.textViewDeviceCount.setText("共 " + totalCount + " 个设备");
            }
        });
    }
    
    /**
     * 设备列表适配器
     */
    private static class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.DeviceViewHolder> {
        
        private List<DeviceListResponse.Device> deviceList = new ArrayList<>();
        
        public void updateDeviceList(List<DeviceListResponse.Device> newDeviceList) {
            this.deviceList.clear();
            if (newDeviceList != null) {
                this.deviceList.addAll(newDeviceList);
            }
            notifyDataSetChanged();
        }
        
        @NonNull
        @Override
        public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(android.R.layout.simple_list_item_2, parent, false);
            return new DeviceViewHolder(view);
        }
        
        @Override
        public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
            DeviceListResponse.Device device = deviceList.get(position);
            holder.bind(device);
        }
        
        @Override
        public int getItemCount() {
            return deviceList.size();
        }
        
        /**
         * 设备项ViewHolder
         */
        static class DeviceViewHolder extends RecyclerView.ViewHolder {
            private TextView textViewDeviceName;
            private TextView textViewDeviceInfo;
            
            public DeviceViewHolder(@NonNull View itemView) {
                super(itemView);
                textViewDeviceName = itemView.findViewById(android.R.id.text1);
                textViewDeviceInfo = itemView.findViewById(android.R.id.text2);
            }
            
            public void bind(DeviceListResponse.Device device) {
                // 显示设备名称(优先显示解码后的alias,如果为空则显示deviceName)
                String decodedAlias = device.getDecodedAlias();
                String displayName = decodedAlias != null && !decodedAlias.isEmpty() 
                        ? decodedAlias : device.getDeviceName();
                textViewDeviceName.setText(displayName);
                
                // 显示设备信息(型号 + MAC地址 + 状态)
                String deviceInfo = String.format("%s | MAC: %s | 状态: %s", 
                        device.getDeviceModel(),
                        device.getDeviceMac(),
                        device.getStatus() == 1 ? "在线" : "离线");
                textViewDeviceInfo.setText(deviceInfo);
                
                // 设置点击事件(可选)
                itemView.setOnClickListener(v -> {
                    // 这里可以添加设备详情页面跳转
                    // Intent intent = new Intent(v.getContext(), DeviceDetailActivity.class);
                    // intent.putExtra("device_id", device.getDeviceId());
                    // v.getContext().startActivity(intent);
                });
            }
        }
    }
}

package com.tplink.deco.viewmodel;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.tplink.deco.client.ApiClient;
import com.tplink.deco.dao.DeviceListApiService;
import com.tplink.deco.model.DeviceListRequest;
import com.tplink.deco.model.DeviceListResponse;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class DeviceListViewModel extends ViewModel {
    
    private DeviceListApiService deviceListApiService;
    private Call<DeviceListResponse> deviceListCall;
    
    // LiveData for device list
    private MutableLiveData<List<DeviceListResponse.Device>> _deviceList = new MutableLiveData<>(new ArrayList<>());
    private MutableLiveData<Integer> _totalDeviceCount = new MutableLiveData<>(0);
    
    // Public LiveData (read-only)
    public LiveData<List<DeviceListResponse.Device>> deviceList = _deviceList;
    public LiveData<Integer> totalDeviceCount = _totalDeviceCount;
    
    public DeviceListViewModel() {
        deviceListApiService = ApiClient.getDeviceListService();
    }
    
    /**
     * 获取设备列表
     * @param token 登录API返回的token
     * @param index 分页索引,从0开始
     * @param limit 每页数量,默认20
     */
    public void getDeviceList(String token, int index, int limit) {
        // 创建请求参数
        DeviceListRequest request = new DeviceListRequest();
        // 设置分页参数
        request.getParams().setIndex(index);
        request.getParams().setLimit(limit);
        
        // 发起网络请求
        deviceListCall = deviceListApiService.getDeviceList(
                "TP-Link_Tapo_Android",
                "2.2.15",
                "wifi", 
                "40-3F-8C-3B-B7-B9",
                "Android%208.1.0",
                "zh_CN_%23Hans",
                token,
                request
        );
        
        deviceListCall.enqueue(new Callback<DeviceListResponse>() {
            @Override
            public void onResponse(Call<DeviceListResponse> call, Response<DeviceListResponse> response) {
                handleDeviceListResponse(response);
            }
            
            @Override
            public void onFailure(Call<DeviceListResponse> call, Throwable t) {
                // 可以在这里处理网络失败,但用户要求不要error message
                // 保持当前设备列表不变
            }
        });
    }
    
    /**
     * 处理设备列表响应
     */
    private void handleDeviceListResponse(Response<DeviceListResponse> response) {
        if (response.isSuccessful() && response.body() != null) {
            DeviceListResponse deviceResponse = response.body();
            
            if (deviceResponse.getError_code() == 0 && deviceResponse.getResult() != null) {
                DeviceListResponse.DeviceListResult result = deviceResponse.getResult();
                
                // 更新设备列表
                List<DeviceListResponse.Device> devices = result.getDeviceList();
                if (devices != null) {
                    _deviceList.setValue(devices);
                    _totalDeviceCount.setValue(result.getTotalNum());
                }
            }
        }
    }
    
    /**
     * 刷新设备列表(重新从第一页开始)
     */
    public void refreshDeviceList(String token) {
        getDeviceList(token, 0, 20);
    }
    
    /**
     * 加载更多设备(分页加载)
     */
    public void loadMoreDevices(String token, int currentSize) {
        int nextIndex = currentSize;
        getDeviceList(token, nextIndex, 20);
    }
    
    /**
     * 追加设备到现有列表(用于分页加载)
     */
    private void appendDevices(List<DeviceListResponse.Device> newDevices) {
        List<DeviceListResponse.Device> currentList = _deviceList.getValue();
        if (currentList != null && newDevices != null) {
            List<DeviceListResponse.Device> updatedList = new ArrayList<>(currentList);
            updatedList.addAll(newDevices);
            _deviceList.setValue(updatedList);
        }
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        // 取消未完成的网络请求
        if (deviceListCall != null && !deviceListCall.isCanceled()) {
            deviceListCall.cancel();
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- 工具栏 -->
    <LinearLayout
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="?attr/colorPrimary"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingStart="16dp"
        android:paddingEnd="16dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="设备列表"
            android:textColor="@android:color/white"
            android:textSize="18sp"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_refresh"
            android:layout_width="wrap_content"
            android:layout_height="36dp"
            android:background="?attr/selectableItemBackground"
            android:text="刷新"
            android:textColor="@android:color/white"
            android:textSize="14sp" />

    </LinearLayout>

    <!-- 设备数量显示 -->
    <TextView
        android:id="@+id/text_view_device_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="共 0 个设备"
        android:textSize="14sp"
        android:textColor="?android:attr/textColorSecondary" />

    <!-- 设备列表 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view_devices"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:padding="8dp" />

    <!-- 空状态提示 -->
    <TextView
        android:id="@+id/text_view_empty_state"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:text="暂无设备\n请确保您的设备已正确配置"
        android:textColor="?android:attr/textColorSecondary"
        android:textSize="16sp"
        android:visibility="gone" />

</LinearLayout>

package com.tplink.deco.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

public class CircularProgressView extends View {
    
    private Paint backgroundPaint;
    private Paint progressPaint;
    private RectF oval;
    private float progress = 0f; // 0-100
    private int strokeWidth = 20;
    
    // 颜色定义
    private static final int BACKGROUND_COLOR = 0xFFE8E8E8; // 灰色背景
    private static final int PROGRESS_COLOR = 0xFF00BCD4;   // 蓝色进度

    public CircularProgressView(Context context) {
        super(context);
        init();
    }

    public CircularProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        // 背景圆环画笔
        backgroundPaint = new Paint();
        backgroundPaint.setAntiAlias(true);
        backgroundPaint.setStyle(Paint.Style.STROKE);
        backgroundPaint.setStrokeWidth(strokeWidth);
        backgroundPaint.setColor(BACKGROUND_COLOR);
        backgroundPaint.setStrokeCap(Paint.Cap.ROUND);

        // 进度圆环画笔
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(strokeWidth);
        progressPaint.setColor(PROGRESS_COLOR);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);

        oval = new RectF();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        int radius = Math.min(centerX, centerY) - strokeWidth / 2;
        
        // 设置绘制区域
        oval.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
        
        // 绘制背景圆环
        canvas.drawCircle(centerX, centerY, radius, backgroundPaint);
        
        // 绘制进度圆环
        float sweepAngle = (progress / 100f) * 360f;
        canvas.drawArc(oval, -90, sweepAngle, false, progressPaint);
    }

    /**
     * 设置进度 0-100
     */
    public void setProgress(float progress) {
        this.progress = Math.max(0, Math.min(100, progress));
        invalidate();
    }

    public float getProgress() {
        return progress;
    }
}

// 2. 固件升级Activity

package com.tplink.deco.activity;

import android.animation.ValueAnimator;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.tplink.deco.R;
import com.tplink.deco.widget.CircularProgressView;

public class FirmwareUpgradeActivity extends AppCompatActivity {
    
    private CircularProgressView progressView;
    private TextView remainingTimeText;
    private TextView statusText;
    private CountDownTimer countDownTimer;
    private ValueAnimator progressAnimator;
    
    // 默认升级时间(秒)
    private static final int UPGRADE_DURATION = 150; // 2分30秒

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_firmware_upgrade);
        
        initViews();
        setupToolbar();
        startUpgrade();
    }

    private void initViews() {
        progressView = findViewById(R.id.progressView);
        remainingTimeText = findViewById(R.id.remainingTimeText);
        statusText = findViewById(R.id.statusText);
    }

    private void setupToolbar() {
        // 设置返回按钮(可选)
        findViewById(R.id.backButton).setOnClickListener(v -> {
            // 升级过程中不允许返回,可以显示警告
            Toast.makeText(this, "固件升级进行中,请勿中断", Toast.LENGTH_SHORT).show();
        });
    }

    private void startUpgrade() {
        statusText.setText("Installing new firmware...");
        
        // 启动倒计时
        startCountdown();
        
        // 启动进度动画
        startProgressAnimation();
    }

    private void startCountdown() {
        countDownTimer = new CountDownTimer(UPGRADE_DURATION * 1000, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                long seconds = millisUntilFinished / 1000;
                long minutes = seconds / 60;
                seconds = seconds % 60;
                
                String timeText = String.format("%02d:%02d", minutes, seconds);
                remainingTimeText.setText(timeText);
            }

            @Override
            public void onFinish() {
                remainingTimeText.setText("00:00");
                onUpgradeComplete();
            }
        };
        
        countDownTimer.start();
    }

    private void startProgressAnimation() {
        progressAnimator = ValueAnimator.ofFloat(0f, 100f);
        progressAnimator.setDuration(UPGRADE_DURATION * 1000);
        progressAnimator.addUpdateListener(animation -> {
            float progress = (Float) animation.getAnimatedValue();
            progressView.setProgress(progress);
        });
        
        progressAnimator.start();
    }

    private void onUpgradeComplete() {
        statusText.setText("Firmware upgrade completed!");
        
        // 延迟2秒后关闭页面
        remainingTimeText.postDelayed(() -> {
            Toast.makeText(this, "升级完成", Toast.LENGTH_SHORT).show();
            finish();
        }, 2000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        // 清理资源
        if (countDownTimer != null) {
            countDownTimer.cancel();
        }
        
        if (progressAnimator != null) {
            progressAnimator.cancel();
        }
    }

    @Override
    public void onBackPressed() {
        // 升级过程中禁用返回键
        Toast.makeText(this, "固件升级进行中,请勿中断", Toast.LENGTH_SHORT).show();
    }
}

// 3. 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/white">

    <!-- 顶部工具栏 -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:paddingStart="16dp"
        android:paddingEnd="16dp">

        <ImageView
            android:id="@+id/backButton"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_centerVertical="true"
            android:src="@drawable/ic_menu"
            android:background="?attr/selectableItemBackgroundBorderless" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="TP WIFI"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="@android:color/black" />

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:src="@drawable/ic_add"
            android:background="?attr/selectableItemBackgroundBorderless" />

    </RelativeLayout>

    <!-- 分割线 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#EEEEEE" />

    <!-- 内容区域 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:padding="24dp">

        <!-- 说明文字 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="The new firmware will be downloaded by your Deco system, not this app. No mobile data or storage on this device is required."
            android:textSize="16sp"
            android:textColor="#666666"
            android:lineSpacing="4dp" />

        <!-- 占位空间 -->
        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

        <!-- 圆形进度条区域 -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center">

            <!-- 圆形进度条 -->
            <com.tplink.deco.widget.CircularProgressView
                android:id="@+id/progressView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:layout_centerHorizontal="true" />

            <!-- 中间文字 -->
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:orientation="vertical"
                android:gravity="center">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Remaining Time"
                    android:textSize="14sp"
                    android:textColor="#999999" />

                <TextView
                    android:id="@+id/remainingTimeText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="00:02:32"
                    android:textSize="24sp"
                    android:textStyle="bold"
                    android:textColor="@android:color/black"
                    android:layout_marginTop="4dp" />

            </LinearLayout>

        </RelativeLayout>

        <!-- 占位空间 -->
        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

        <!-- 状态文字 -->
        <TextView
            android:id="@+id/statusText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Installing new firmware..."
            android:textSize="16sp"
            android:textColor="#666666" />

        <!-- 底部占位 -->
        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.5" />

    </LinearLayout>

</LinearLayout>

// 4. 启动升级界面的方法 // 在你的设备列表或设备详情页面中添加:

public void startFirmwareUpgrade() {
    Intent intent = new Intent(this, FirmwareUpgradeActivity.class);
    startActivity(intent);
}

// 5. 需要的图标资源 // 在 app/src/main/res/drawable/ 目录下创建:

// ic_menu.xml (汉堡菜单图标)
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M3,6h18v2H3V6zM3,11h18v2H3V11zM3,16h18v2H3V16z" />
</vector>

// ic_add.xml (加号图标)
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>

// 6. 使用说明和特性 /* ✨ 主要特性:

  1. 圆形进度条从0%到100%平滑填充
  2. 倒计时从设定时间倒数到00:00
  3. 进度条和倒计时同步进行
  4. 升级完成后自动关闭页面
  5. 升级过程中禁用返回键

🎨 视觉效果:

  • 蓝色进度条(#00BCD4)逐渐填充灰色背景圆环
  • 圆角笔触,看起来更现代
  • 中心显示剩余时间,格式MM:SS
  • 底部显示当前状态文字

🔧 自定义选项:

  • 修改UPGRADE_DURATION调整升级时长
  • 修改PROGRESS_COLOR和BACKGROUND_COLOR调整颜色
  • 修改strokeWidth调整圆环粗细

📱 启动方式: 在设备列表点击升级按钮时调用startFirmwareUpgrade()方法 */# 🎯 设计完成!

我为你设计了一个完整的固件升级界面,完美还原了截图中的效果:

✨ 主要特性

🔄 圆形进度条

  • 自定义View - 平滑的圆环进度动画
  • 颜色渐变 - 蓝色进度逐渐填充灰色背景
  • 同步更新 - 进度和倒计时完美同步

⏱️ 倒计时功能

  • 实时更新 - 每秒更新显示MM:SS格式
  • 默认2分30秒 - 可以通过UPGRADE_DURATION调整
  • 倒计时结束 - 自动显示完成状态

🎨 界面设计

  • 完全还原截图 - 包括顶部工具栏、说明文字、进度区域
  • 现代化UI - 圆角笔触、合适的间距和颜色
  • 状态提示 - 底部动态显示当前状态

🛡️ 用户体验

  • 防中断 - 升级过程中禁用返回键
  • 自动完成 - 升级结束后自动关闭页面
  • 友好提示 - 用户尝试返回时显示警告

🚀 使用方法

// 在设备列表或详情页面启动升级
public void onUpgradeButtonClick() {
    Intent intent = new Intent(this, FirmwareUpgradeActivity.class);
    startActivity(intent);
}

🎛️ 自定义选项

  • 升级时长: 修改UPGRADE_DURATION常量
  • 进度颜色: 修改PROGRESS_COLORBACKGROUND_COLOR
  • 圆环粗细: 调整strokeWidth参数
package com.tplink.deco.model;

import java.util.Arrays;
import java.util.List;

public class DeviceListRequest {
    private String method;
    private DeviceListParams params;

    public DeviceListRequest() {
        this.method = "getDeviceListByPage";
        this.params = new DeviceListParams();
    }

    public static class DeviceListParams {
        private List<String> deviceTypeList;
        private int index;
        private int limit;

        public DeviceListParams() {
            this.deviceTypeList = Arrays.asList("SMART.TAPOPLUG");
            this.index = 0;
            this.limit = 20;
        }

        // getters and setters
        public List<String> getDeviceTypeList() { return deviceTypeList; }
        public void setDeviceTypeList(List<String> deviceTypeList) { this.deviceTypeList = deviceTypeList; }
        public int getIndex() { return index; }
        public void setIndex(int index) { this.index = index; }
        public int getLimit() { return limit; }
        public void setLimit(int limit) { this.limit = limit; }
    }

    // getters and setters
    public String getMethod() { return method; }
    public void setMethod(String method) { this.method = method; }
    public DeviceListParams getParams() { return params; }
    public void setParams(DeviceListParams params) { this.params = params; }}

// 2. 设备响应实体

package com.tplink.deco.model;

import java.util.List;

public class DeviceListResponse {
    private int error_code;
    private DeviceListResult result;

    public static class DeviceListResult {
        private int totalNum;
        private List<Device> deviceList;
        private int currentIndex;

        // getters and setters
        public int getTotalNum() { return totalNum; }
        public void setTotalNum(int totalNum) { this.totalNum = totalNum; }
        public List<Device> getDeviceList() { return deviceList; }
        public void setDeviceList(List<Device> deviceList) { this.deviceList = deviceList; }
        public int getCurrentIndex() { return currentIndex; }
        public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; }
    }

    public static class Device {
        private String deviceType;
        private int role;
        private String deviceId;
        private String deviceName;
        private String alias;
        private String deviceMac;
        private String deviceModel;
        private int status;

        // 解码别名
        public String getDecodedAlias() {
            try {
                if (alias != null) {
                    return new String(android.util.Base64.decode(alias, android.util.Base64.DEFAULT));
                }
            } catch (Exception e) {}
            return alias;
        }

        // getters and setters
        public String getDeviceType() { return deviceType; }
        public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
        public int getRole() { return role; }
        public void setRole(int role) { this.role = role; }
        public String getDeviceId() { return deviceId; }
        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
        public String getDeviceName() { return deviceName; }
        public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
        public String getAlias() { return alias; }
        public void setAlias(String alias) { this.alias = alias; }
        public String getDeviceMac() { return deviceMac; }
        public void setDeviceMac(String deviceMac) { this.deviceMac = deviceMac; }
        public String getDeviceModel() { return deviceModel; }
        public void setDeviceModel(String deviceModel) { this.deviceModel = deviceModel; }
        public int getStatus() { return status; }
        public void setStatus(int status) { this.status = status; }
    }

    // getters and setters
    public int getError_code() { return error_code; }
    public void setError_code(int error_code) { this.error_code = error_code; }
    public DeviceListResult getResult() { return result; }
    public void setResult(DeviceListResult result) { this.result = result; }
}

// 3. 设备API接口

package com.tplink.deco.dao;

import com.tplink.deco.model.DeviceListRequest;
import com.tplink.deco.model.DeviceListResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Query;

public interface DeviceApiService {
    @POST("/")
    Call<DeviceListResponse> getDeviceList(
        @Query("appName") String appName,
        @Query("appVer") String appVer,
        @Query("netType") String netType,
        @Query("termID") String termID,
        @Query("ospf") String ospf,
        @Query("locale") String locale,
        @Query("token") String token,
        @Body DeviceListRequest request
    );
}

// 4. 在ApiClient中新增方法(不动原有代码) // 在你的ApiClient类中添加这个方法:

public static DeviceApiService getDeviceService() {
    // 设备API不需要SignatureInterceptor,单独创建
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(logging)
            .sslSocketFactory(createSSLSocketFactory(), createTrustManager())
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    return retrofit.create(DeviceApiService.class);
}

// 5. HomePageActivity关键代码

package com.tplink.deco.activity;

import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.tplink.deco.client.ApiClient;
import com.tplink.deco.dao.DeviceApiService;
import com.tplink.deco.model.DeviceListRequest;
import com.tplink.deco.model.DeviceListResponse;
import com.tplink.deco.adapter.DeviceAdapter;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.util.ArrayList;

public class HomePageActivity extends AppCompatActivity {
    
    private DeviceApiService deviceApiService;
    private RecyclerView recyclerView;
    private DeviceAdapter adapter;
    private String userToken;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_homepage);
        
        // 获取登录传递的token
        userToken = getIntent().getStringExtra("user_token");
        
        // 初始化
        deviceApiService = ApiClient.getDeviceService();
        recyclerView = findViewById(R.id.recyclerView);
        adapter = new DeviceAdapter(new ArrayList<>());
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
        
        // 按钮点击事件
        findViewById(R.id.btn_network).setOnClickListener(v -> loadDevices());
    }

    private void loadDevices() {
        DeviceListRequest request = new DeviceListRequest();
        
        Call<DeviceListResponse> call = deviceApiService.getDeviceList(
                "TP-Link_Tapo_Android",
                "2.2.15",
                "wifi",
                "40-3F-8C-3B-B7-B9",  // 固定的termID
                "Android 8.1.0",
                "zh_CN_#Hans",
                userToken,
                request
        );
        
        call.enqueue(new Callback<DeviceListResponse>() {
            @Override
            public void onResponse(Call<DeviceListResponse> call, Response<DeviceListResponse> response) {
                if (response.isSuccessful() && response.body() != null) {
                    DeviceListResponse deviceResponse = response.body();
                    if (deviceResponse.getError_code() == 0 && deviceResponse.getResult() != null) {
                        adapter.updateDevices(deviceResponse.getResult().getDeviceList());
                    } else {
                        Toast.makeText(HomePageActivity.this, "获取设备失败", Toast.LENGTH_SHORT).show();
                    }
                }
            }

            @Override
            public void onFailure(Call<DeviceListResponse> call, Throwable t) {
                Toast.makeText(HomePageActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

// 6. 简单的设备适配器

package com.tplink.deco.adapter;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.tplink.deco.R;
import com.tplink.deco.model.DeviceListResponse;
import java.util.List;

public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder> {
    
    private List<DeviceListResponse.Device> devices;

    public DeviceAdapter(List<DeviceListResponse.Device> devices) {
        this.devices = devices;
    }

    public void updateDevices(List<DeviceListResponse.Device> newDevices) {
        this.devices = newDevices;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_device, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        DeviceListResponse.Device device = devices.get(position);
        holder.deviceName.setText(device.getDeviceName());
        holder.deviceAlias.setText(device.getDecodedAlias());
    }

    @Override
    public int getItemCount() {
        return devices.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView deviceName, deviceAlias;

        ViewHolder(View itemView) {
            super(itemView);
            deviceName = itemView.findViewById(R.id.deviceName);
            deviceAlias = itemView.findViewById(R.id.deviceAlias);
        }
    }
}

// 7. 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btn_network"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取设备" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="16dp" />

</LinearLayout>

// item_device.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="?attr/selectableItemBackground">

    <TextView
        android:id="@+id/deviceName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/deviceAlias"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:layout_marginTop="4dp" />

</LinearLayout>