package com.paul.drone.activity; import android.annotation.SuppressLint; import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import com.paul.drone.MainActivity; import com.paul.drone.R; import com.paul.drone.data.UpdateInfoResponse; import com.paul.drone.manager.DJISDKManager; import com.paul.drone.network.SessionManager; import com.paul.drone.repository.NetworkRepository; import com.paul.drone.util.VersionUpdateUtil; import java.io.File; import java.util.Objects; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.schedulers.Schedulers; public class LoginActivity extends AppCompatActivity { private static final String TAG = LoginActivity.class.getSimpleName(); private NetworkRepository networkRepository; private VersionUpdateUtil updateUtil; private EditText editTextPhone; private TextView tvSdkInitStatus; private EditText editTextSmsCode; private Button buttonSendSms; private Button buttonLogin; private CompositeDisposable disposables = new CompositeDisposable(); // 声明需要的成员变量 private long downloadId; private ProgressBar progressBar; private TextView progressText; private TextView statusText; private Button installButton; private AlertDialog dialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化 NetworkRepository networkRepository = NetworkRepository.getInstance(); SessionManager sessionManager = new SessionManager(this); updateUtil = VersionUpdateUtil.getInstance(this); String currentVersion = updateUtil.getCurrentVersion(); Single updateInfoResponseSingle = updateUtil.checkLatestVersion(); disposables.add(updateInfoResponseSingle.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( updateInfoResponse -> { boolean newVersion = updateUtil.isNewVersion(currentVersion, updateInfoResponse.getData().getVersion()); if (newVersion && updateInfoResponse.getData().isForce()) { // 处理强制更新逻辑 - 显示更新对话框 showForceUpdateDialog(updateInfoResponse); } else { // 没有强制更新,继续检查登录状态 checkLoginStatus(sessionManager); } }, throwable -> { // 处理错误 - 即使检查失败也允许用户登录 Log.e(TAG, "版本检查失败: " + throwable.getMessage(), throwable); checkLoginStatus(sessionManager); } )); // 注意:不要在这里添加其他逻辑,所有后续操作应该在subscribe回调中处理 } private void checkLoginStatus(SessionManager sessionManager) { // 检查登录状态 if (!sessionManager.isSessionExpired()) { Log.i(TAG, "用户已登录且token有效,直接进入MainActivity"); Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); finish(); return; } else { Log.i(TAG, "未登录或token无效,显示登录界面"); } // 未登录或token无效,加载登录界面 setContentView(R.layout.activity_login); initViews(); setupListeners(); } private void initViews() { editTextPhone = findViewById(R.id.et_phone_number); editTextSmsCode = findViewById(R.id.et_sms_code); buttonSendSms = findViewById(R.id.btn_send_sms_code); buttonLogin = findViewById(R.id.btn_login); tvSdkInitStatus = findViewById(R.id.tv_sdk_init_status); } private void setupListeners() { // 发送验证码按钮点击事件 buttonSendSms.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tvSdkInitStatus.setText(DJISDKManager.getInstance().isSDKRegistered() ? "Dji SDK 初始化成功" : "Dji SDK 初始化中。。。。"); String phoneNumber = editTextPhone.getText().toString().trim(); if (TextUtils.isEmpty(phoneNumber)) { Toast.makeText(LoginActivity.this, "请输入手机号", Toast.LENGTH_SHORT).show(); return; } if (phoneNumber.length() != 11) { Toast.makeText(LoginActivity.this, "请输入正确的手机号", Toast.LENGTH_SHORT).show(); return; } sendSmsCode(phoneNumber); } }); // 登录按钮点击事件 buttonLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String phoneNumber = editTextPhone.getText().toString().trim(); String smsCode = editTextSmsCode.getText().toString().trim(); if (TextUtils.isEmpty(phoneNumber)) { Toast.makeText(LoginActivity.this, "请输入手机号", Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(smsCode)) { Toast.makeText(LoginActivity.this, "请输入验证码", Toast.LENGTH_SHORT).show(); return; } if (!DJISDKManager.getInstance().isSDKInitialized()) { Toast.makeText(LoginActivity.this, "SDK 未初始化,请稍后再试", Toast.LENGTH_SHORT).show(); return; } performLogin(phoneNumber, smsCode); } }); } /** * 发送验证码 */ private void sendSmsCode(String phoneNumber) { Log.i(TAG, "发送验证码到: " + phoneNumber); // 禁用发送按钮 buttonSendSms.setEnabled(false); buttonSendSms.setText("发送中..."); // 使用 NetworkRepository 发送验证码 disposables.add(networkRepository.sendSmsCode(phoneNumber) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( response -> { if (response.getCode() == 200){ buttonSendSms.setEnabled(true); buttonSendSms.setText("发送验证码"); Toast.makeText(LoginActivity.this, "验证码已发送", Toast.LENGTH_SHORT).show(); }else{ buttonSendSms.setEnabled(true); buttonSendSms.setText("发送验证码"); Toast.makeText(LoginActivity.this, "发送验证码错误:"+response.getMsg(), Toast.LENGTH_SHORT).show(); } }, throwable -> { buttonSendSms.setEnabled(true); buttonSendSms.setText("发送验证码"); String errorMessage = getErrorMessage(throwable); Toast.makeText(LoginActivity.this, "发送失败: " + errorMessage, Toast.LENGTH_SHORT).show(); } )); } /** * 执行登录 */ private void performLogin(String phoneNumber, String smsCode) { Log.i(TAG, "登录: " + phoneNumber + ", 验证码: " + smsCode); // 禁用登录按钮 buttonLogin.setEnabled(false); buttonLogin.setText("登录中..."); // 使用 NetworkRepository 执行登录 disposables.add(networkRepository.loginWithSmsCode(phoneNumber, smsCode) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( response -> { buttonLogin.setEnabled(true); buttonLogin.setText("登录"); if (response != null) { Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); // 跳转到 MainActivity Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); finish(); } else { Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show(); } }, throwable -> { buttonLogin.setEnabled(true); buttonLogin.setText("登录"); String errorMessage = getErrorMessage(throwable); Toast.makeText(LoginActivity.this, "登录失败: " + errorMessage, Toast.LENGTH_SHORT).show(); } )); } /** * 统一错误处理 */ private String getErrorMessage(Throwable throwable) { if (throwable instanceof java.io.IOException) { return "网络连接失败,请检查网络设置"; } else { return throwable.getMessage() != null ? throwable.getMessage() : "未知错误"; } } private void showForceUpdateDialog(UpdateInfoResponse updateInfo) { AlertDialog.Builder builder = new AlertDialog.Builder(this); LayoutInflater inflater = LayoutInflater.from(this); View dialogView = inflater.inflate(R.layout.dialog_update, null); builder.setView(dialogView); AlertDialog dialog = builder.create(); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); // 获取对话框中的控件 TextView currentVersionText = dialogView.findViewById(R.id.currentVersion); TextView newVersionText = dialogView.findViewById(R.id.newVersion); ProgressBar progressBar = dialogView.findViewById(R.id.dialogProgressBar); TextView progressText = dialogView.findViewById(R.id.dialogProgressText); TextView statusText = dialogView.findViewById(R.id.dialogStatusText); Button cancelButton = dialogView.findViewById(R.id.dialogCancelButton); Button installButton = dialogView.findViewById(R.id.dialogInstallButton); // 设置版本信息 String currentVersion = updateUtil.getCurrentVersion(); currentVersionText.setText(currentVersion); newVersionText.setText(updateInfo.getVersion()); // 设置状态文本 statusText.setText("检测到重要更新,请立即升级"); // 隐藏进度条和进度文本(初始状态) progressBar.setVisibility(View.GONE); progressText.setVisibility(View.GONE); // 隐藏取消按钮,显示立即更新按钮 cancelButton.setVisibility(View.GONE); installButton.setVisibility(View.VISIBLE); installButton.setText("立即更新"); // 设置立即更新按钮点击事件 installButton.setOnClickListener(v -> { // 开始下载APK startDownloadApk(updateInfo, dialog, progressBar, progressText, statusText, installButton); }); dialog.show(); } // 修改 startDownloadApk 方法中的安装按钮点击事件 private final Handler progressHandler = new Handler(Looper.getMainLooper()); private Runnable progressRunnable; private boolean isDownloading = false; // 修改 startDownloadApk 方法 @SuppressLint("UnspecifiedRegisterReceiverFlag") private void startDownloadApk(UpdateInfoResponse updateInfo, AlertDialog dialog, ProgressBar progressBar, TextView progressText, TextView statusText, Button installButton) { // 保存引用到成员变量 this.dialog = dialog; this.progressBar = progressBar; this.progressText = progressText; this.statusText = statusText; this.installButton = installButton; // 显示进度条 progressBar.setVisibility(View.VISIBLE); progressText.setVisibility(View.VISIBLE); statusText.setText("正在准备下载..."); installButton.setEnabled(false); installButton.setText("下载中..."); try { // 使用VersionUpdateUtil下载APK downloadId = updateUtil.downloadApk(updateInfo.getUpgradeurl(), updateInfo.getVersion()); statusText.setText("开始下载,请勿关闭应用..."); progressText.setText("下载已启动..."); // 先检查是否已经有相同下载任务在进行或已完成 if (isDownloadAlreadyExists(updateInfo.getUpgradeurl())) { Log.w(TAG, "检测到相同URL的下载任务已存在"); } // 注册下载完成广播接收器 IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(downloadReceiver, filter, Context.RECEIVER_EXPORTED); } else { registerReceiver(downloadReceiver, filter); Log.d(TAG, "已注册广播接收器"); } // 开始定期查询下载进度 startProgressTracking(); } catch (Exception e) { statusText.setText("下载失败: " + e.getMessage()); installButton.setEnabled(true); installButton.setText("重试"); installButton.setOnClickListener(v -> { startDownloadApk(updateInfo, dialog, progressBar, progressText, statusText, installButton); }); } } // 添加检查下载任务是否已存在的方法 private boolean isDownloadAlreadyExists(String downloadUrl) { DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); Cursor cursor = downloadManager.query(query); if (cursor != null) { int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_URI); while (cursor.moveToNext()) { String uri = cursor.getString(uriIndex); if (downloadUrl.equals(uri)) { cursor.close(); return true; } } cursor.close(); } return false; } // 添加进度跟踪方法 private void startProgressTracking() { isDownloading = true; progressRunnable = new Runnable() { @Override public void run() { if (isDownloading) { updateProgress(); progressHandler.postDelayed(this, 1000); // 每秒更新一次 } } }; progressHandler.post(progressRunnable); } // 更新进度显示 private void updateProgress() { DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); Cursor cursor = downloadManager.query(query); if (cursor != null && cursor.moveToFirst()) { int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); int status = cursor.getInt(statusIndex); switch (status) { case DownloadManager.STATUS_RUNNING: int bytesDownloadedIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR); int bytesTotalIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES); long bytesDownloaded = cursor.getLong(bytesDownloadedIndex); long bytesTotal = cursor.getLong(bytesTotalIndex); if (bytesTotal > 0) { int progress = (int) ((bytesDownloaded * 100) / bytesTotal); progressBar.setProgress(progress); progressText.setText(String.format("下载中... %d%%", progress)); statusText.setText("正在下载应用更新..."); } break; case DownloadManager.STATUS_PENDING: statusText.setText("等待下载开始..."); progressText.setText("准备中..."); break; case DownloadManager.STATUS_PAUSED: statusText.setText("下载已暂停"); progressText.setText("下载暂停"); break; case DownloadManager.STATUS_FAILED: statusText.setText("下载失败"); progressText.setText("下载失败,请重试"); stopProgressTracking(); // 显示重试按钮 installButton.setEnabled(true); installButton.setText("重试"); installButton.setOnClickListener(v -> { // 重新开始下载逻辑 }); break; } } if (cursor != null) { cursor.close(); } } // 停止进度跟踪 private void stopProgressTracking() { isDownloading = false; if (progressRunnable != null) { progressHandler.removeCallbacks(progressRunnable); } } // 修改 downloadReceiver 添加额外验证 private final BroadcastReceiver downloadReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "收到广播: " + intent.getAction()); if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) { long receivedDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); Log.d(TAG, "下载ID: " + receivedDownloadId + ", 期望ID: " + downloadId); // 额外验证:确保是当前会话的下载任务 if (receivedDownloadId == downloadId && downloadId != 0) { Log.d(TAG, "下载完成,准备更新UI"); // 停止进度跟踪 stopProgressTracking(); // 下载完成,更新UI runOnUiThread(() -> { if (!isFinishing() && progressText != null && statusText != null && installButton != null) { progressText.setText(""); statusText.setText("下载已完成,准备安装"); progressBar.setProgress(100); installButton.setEnabled(true); installButton.setText("立即安装"); installButton.setVisibility(View.VISIBLE); // 设置安装按钮点击事件 installButton.setOnClickListener(installView -> { installDownloadedApk(downloadId, dialog); }); Log.d(TAG, "UI更新完成"); } else { Log.w(TAG, "Activity已结束或UI组件为空"); } }); } else { Log.d(TAG, "收到其他下载任务的完成广播或无效下载ID"); } } } }; // 在 onDestroy 中停止进度跟踪 @Override protected void onDestroy() { super.onDestroy(); // 停止进度跟踪 stopProgressTracking(); // 清理资源 if (disposables != null && !disposables.isDisposed()) { disposables.dispose(); } // 注销广播接收器 try { unregisterReceiver(downloadReceiver); Log.d(TAG, "广播接收器已注销"); } catch (IllegalArgumentException e) { // 接收器未注册,忽略 Log.w(TAG, "广播接收器未注册或已注销: " + e.getMessage()); } } private void installDownloadedApk(long downloadId, AlertDialog dialog) { try { // 通过DownloadManager查询下载的文件 DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); Cursor cursor = downloadManager.query(query); if (cursor != null && cursor.moveToFirst()) { int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(statusIndex)) { // 获取下载文件的URI int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI); String downloadedFileUriString = cursor.getString(uriIndex); if (downloadedFileUriString != null) { Uri apkUri = Uri.parse(downloadedFileUriString); // Android 7.0及以上版本使用FileProvider处理file:// URI if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && "file".equals(apkUri.getScheme())) { // 将 file:// URI 转换为 content:// URI File apkFile = new File(apkUri.getPath()); if (apkFile.exists()) { Uri contentUri = androidx.core.content.FileProvider.getUriForFile( this, "com.paul.drone.fileprovider", apkFile ); // 授予临时权限给包管理器 updateUtil.installApk(contentUri); } else { Toast.makeText(this, "APK文件不存在", Toast.LENGTH_LONG).show(); } } else { // Android 7.0以下版本或已经是content:// URI,直接使用 updateUtil.installApk(apkUri); } dialog.dismiss(); finish(); } else { Toast.makeText(this, "无法找到下载的文件", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, "下载未完成", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, "下载查询失败", Toast.LENGTH_LONG).show(); } if (cursor != null) { cursor.close(); } } catch (Exception e) { Log.e(TAG, "安装APK失败: " + e.getMessage(), e); Toast.makeText(this, "安装失败: " + e.getMessage(), Toast.LENGTH_LONG).show(); } } }