LoginActivity.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. package com.paul.drone.activity;
  2. import android.annotation.SuppressLint;
  3. import android.app.DownloadManager;
  4. import android.content.BroadcastReceiver;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.content.IntentFilter;
  8. import android.database.Cursor;
  9. import android.net.Uri;
  10. import android.os.Build;
  11. import android.os.Bundle;
  12. import android.os.Handler;
  13. import android.os.Looper;
  14. import android.text.TextUtils;
  15. import android.util.Log;
  16. import android.view.LayoutInflater;
  17. import android.view.View;
  18. import android.widget.Button;
  19. import android.widget.EditText;
  20. import android.widget.LinearLayout;
  21. import android.widget.ProgressBar;
  22. import android.widget.TextView;
  23. import android.widget.Toast;
  24. import androidx.appcompat.app.AlertDialog;
  25. import androidx.appcompat.app.AppCompatActivity;
  26. import com.paul.drone.MainActivity;
  27. import com.paul.drone.R;
  28. import com.paul.drone.data.UpdateInfoResponse;
  29. import com.paul.drone.manager.DJISDKManager;
  30. import com.paul.drone.network.SessionManager;
  31. import com.paul.drone.repository.NetworkRepository;
  32. import com.paul.drone.util.VersionUpdateUtil;
  33. import java.io.File;
  34. import java.util.Objects;
  35. import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
  36. import io.reactivex.rxjava3.core.Single;
  37. import io.reactivex.rxjava3.disposables.CompositeDisposable;
  38. import io.reactivex.rxjava3.schedulers.Schedulers;
  39. public class LoginActivity extends AppCompatActivity {
  40. private static final String TAG = LoginActivity.class.getSimpleName();
  41. private NetworkRepository networkRepository;
  42. private VersionUpdateUtil updateUtil;
  43. private EditText editTextPhone;
  44. private TextView tvSdkInitStatus;
  45. private EditText editTextSmsCode;
  46. private Button buttonSendSms;
  47. private Button buttonLogin;
  48. private CompositeDisposable disposables = new CompositeDisposable();
  49. // 声明需要的成员变量
  50. private long downloadId;
  51. private ProgressBar progressBar;
  52. private TextView progressText;
  53. private TextView statusText;
  54. private Button installButton;
  55. private AlertDialog dialog;
  56. @Override
  57. protected void onCreate(Bundle savedInstanceState) {
  58. super.onCreate(savedInstanceState);
  59. // 初始化 NetworkRepository
  60. networkRepository = NetworkRepository.getInstance();
  61. SessionManager sessionManager = new SessionManager(this);
  62. updateUtil = VersionUpdateUtil.getInstance(this);
  63. String currentVersion = updateUtil.getCurrentVersion();
  64. Single<UpdateInfoResponse> updateInfoResponseSingle = updateUtil.checkLatestVersion();
  65. disposables.add(updateInfoResponseSingle.subscribeOn(Schedulers.io())
  66. .observeOn(AndroidSchedulers.mainThread())
  67. .subscribe(
  68. updateInfoResponse -> {
  69. boolean newVersion = updateUtil.isNewVersion(currentVersion, updateInfoResponse.getData().getVersion());
  70. if (newVersion && updateInfoResponse.getData().isForce()) {
  71. // 处理强制更新逻辑 - 显示更新对话框
  72. showForceUpdateDialog(updateInfoResponse);
  73. } else {
  74. // 没有强制更新,继续检查登录状态
  75. checkLoginStatus(sessionManager);
  76. }
  77. },
  78. throwable -> {
  79. // 处理错误 - 即使检查失败也允许用户登录
  80. Log.e(TAG, "版本检查失败: " + throwable.getMessage(), throwable);
  81. checkLoginStatus(sessionManager);
  82. }
  83. ));
  84. // 注意:不要在这里添加其他逻辑,所有后续操作应该在subscribe回调中处理
  85. }
  86. private void checkLoginStatus(SessionManager sessionManager) {
  87. // 检查登录状态
  88. if (!sessionManager.isSessionExpired()) {
  89. Log.i(TAG, "用户已登录且token有效,直接进入MainActivity");
  90. Intent intent = new Intent(LoginActivity.this, MainActivity.class);
  91. startActivity(intent);
  92. finish();
  93. return;
  94. } else {
  95. Log.i(TAG, "未登录或token无效,显示登录界面");
  96. }
  97. // 未登录或token无效,加载登录界面
  98. setContentView(R.layout.activity_login);
  99. initViews();
  100. setupListeners();
  101. }
  102. private void initViews() {
  103. editTextPhone = findViewById(R.id.et_phone_number);
  104. editTextSmsCode = findViewById(R.id.et_sms_code);
  105. buttonSendSms = findViewById(R.id.btn_send_sms_code);
  106. buttonLogin = findViewById(R.id.btn_login);
  107. tvSdkInitStatus = findViewById(R.id.tv_sdk_init_status);
  108. }
  109. private void setupListeners() {
  110. // 发送验证码按钮点击事件
  111. buttonSendSms.setOnClickListener(new View.OnClickListener() {
  112. @Override
  113. public void onClick(View v) {
  114. tvSdkInitStatus.setText(DJISDKManager.getInstance().isSDKRegistered() ? "Dji SDK 初始化成功" : "Dji SDK 初始化中。。。。");
  115. String phoneNumber = editTextPhone.getText().toString().trim();
  116. if (TextUtils.isEmpty(phoneNumber)) {
  117. Toast.makeText(LoginActivity.this, "请输入手机号", Toast.LENGTH_SHORT).show();
  118. return;
  119. }
  120. if (phoneNumber.length() != 11) {
  121. Toast.makeText(LoginActivity.this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
  122. return;
  123. }
  124. sendSmsCode(phoneNumber);
  125. }
  126. });
  127. // 登录按钮点击事件
  128. buttonLogin.setOnClickListener(new View.OnClickListener() {
  129. @Override
  130. public void onClick(View v) {
  131. String phoneNumber = editTextPhone.getText().toString().trim();
  132. String smsCode = editTextSmsCode.getText().toString().trim();
  133. if (TextUtils.isEmpty(phoneNumber)) {
  134. Toast.makeText(LoginActivity.this, "请输入手机号", Toast.LENGTH_SHORT).show();
  135. return;
  136. }
  137. if (TextUtils.isEmpty(smsCode)) {
  138. Toast.makeText(LoginActivity.this, "请输入验证码", Toast.LENGTH_SHORT).show();
  139. return;
  140. }
  141. if (!DJISDKManager.getInstance().isSDKInitialized()) {
  142. Toast.makeText(LoginActivity.this, "SDK 未初始化,请稍后再试", Toast.LENGTH_SHORT).show();
  143. return;
  144. }
  145. performLogin(phoneNumber, smsCode);
  146. }
  147. });
  148. }
  149. /**
  150. * 发送验证码
  151. */
  152. private void sendSmsCode(String phoneNumber) {
  153. Log.i(TAG, "发送验证码到: " + phoneNumber);
  154. // 禁用发送按钮
  155. buttonSendSms.setEnabled(false);
  156. buttonSendSms.setText("发送中...");
  157. // 使用 NetworkRepository 发送验证码
  158. disposables.add(networkRepository.sendSmsCode(phoneNumber)
  159. .subscribeOn(Schedulers.io())
  160. .observeOn(AndroidSchedulers.mainThread())
  161. .subscribe(
  162. response -> {
  163. if (response.getCode() == 200){
  164. buttonSendSms.setEnabled(true);
  165. buttonSendSms.setText("发送验证码");
  166. Toast.makeText(LoginActivity.this, "验证码已发送", Toast.LENGTH_SHORT).show();
  167. }else{
  168. buttonSendSms.setEnabled(true);
  169. buttonSendSms.setText("发送验证码");
  170. Toast.makeText(LoginActivity.this, "发送验证码错误:"+response.getMsg(), Toast.LENGTH_SHORT).show();
  171. }
  172. },
  173. throwable -> {
  174. buttonSendSms.setEnabled(true);
  175. buttonSendSms.setText("发送验证码");
  176. String errorMessage = getErrorMessage(throwable);
  177. Toast.makeText(LoginActivity.this, "发送失败: " + errorMessage, Toast.LENGTH_SHORT).show();
  178. }
  179. ));
  180. }
  181. /**
  182. * 执行登录
  183. */
  184. private void performLogin(String phoneNumber, String smsCode) {
  185. Log.i(TAG, "登录: " + phoneNumber + ", 验证码: " + smsCode);
  186. // 禁用登录按钮
  187. buttonLogin.setEnabled(false);
  188. buttonLogin.setText("登录中...");
  189. // 使用 NetworkRepository 执行登录
  190. disposables.add(networkRepository.loginWithSmsCode(phoneNumber, smsCode)
  191. .subscribeOn(Schedulers.io())
  192. .observeOn(AndroidSchedulers.mainThread())
  193. .subscribe(
  194. response -> {
  195. buttonLogin.setEnabled(true);
  196. buttonLogin.setText("登录");
  197. if (response != null) {
  198. Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
  199. // 跳转到 MainActivity
  200. Intent intent = new Intent(LoginActivity.this, MainActivity.class);
  201. startActivity(intent);
  202. finish();
  203. } else {
  204. Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
  205. }
  206. },
  207. throwable -> {
  208. buttonLogin.setEnabled(true);
  209. buttonLogin.setText("登录");
  210. String errorMessage = getErrorMessage(throwable);
  211. Toast.makeText(LoginActivity.this, "登录失败: " + errorMessage, Toast.LENGTH_SHORT).show();
  212. }
  213. ));
  214. }
  215. /**
  216. * 统一错误处理
  217. */
  218. private String getErrorMessage(Throwable throwable) {
  219. if (throwable instanceof java.io.IOException) {
  220. return "网络连接失败,请检查网络设置";
  221. } else {
  222. return throwable.getMessage() != null ? throwable.getMessage() : "未知错误";
  223. }
  224. }
  225. private void showForceUpdateDialog(UpdateInfoResponse updateInfo) {
  226. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  227. LayoutInflater inflater = LayoutInflater.from(this);
  228. View dialogView = inflater.inflate(R.layout.dialog_update, null);
  229. builder.setView(dialogView);
  230. AlertDialog dialog = builder.create();
  231. dialog.setCancelable(false);
  232. dialog.setCanceledOnTouchOutside(false);
  233. // 获取对话框中的控件
  234. TextView currentVersionText = dialogView.findViewById(R.id.currentVersion);
  235. TextView newVersionText = dialogView.findViewById(R.id.newVersion);
  236. ProgressBar progressBar = dialogView.findViewById(R.id.dialogProgressBar);
  237. TextView progressText = dialogView.findViewById(R.id.dialogProgressText);
  238. TextView statusText = dialogView.findViewById(R.id.dialogStatusText);
  239. Button cancelButton = dialogView.findViewById(R.id.dialogCancelButton);
  240. Button installButton = dialogView.findViewById(R.id.dialogInstallButton);
  241. // 设置版本信息
  242. String currentVersion = updateUtil.getCurrentVersion();
  243. currentVersionText.setText(currentVersion);
  244. newVersionText.setText(updateInfo.getVersion());
  245. // 设置状态文本
  246. statusText.setText("检测到重要更新,请立即升级");
  247. // 隐藏进度条和进度文本(初始状态)
  248. progressBar.setVisibility(View.GONE);
  249. progressText.setVisibility(View.GONE);
  250. // 隐藏取消按钮,显示立即更新按钮
  251. cancelButton.setVisibility(View.GONE);
  252. installButton.setVisibility(View.VISIBLE);
  253. installButton.setText("立即更新");
  254. // 设置立即更新按钮点击事件
  255. installButton.setOnClickListener(v -> {
  256. // 开始下载APK
  257. startDownloadApk(updateInfo, dialog, progressBar, progressText, statusText, installButton);
  258. });
  259. dialog.show();
  260. }
  261. // 修改 startDownloadApk 方法中的安装按钮点击事件
  262. private final Handler progressHandler = new Handler(Looper.getMainLooper());
  263. private Runnable progressRunnable;
  264. private boolean isDownloading = false;
  265. // 修改 startDownloadApk 方法
  266. @SuppressLint("UnspecifiedRegisterReceiverFlag")
  267. private void startDownloadApk(UpdateInfoResponse updateInfo, AlertDialog dialog,
  268. ProgressBar progressBar, TextView progressText,
  269. TextView statusText, Button installButton) {
  270. // 保存引用到成员变量
  271. this.dialog = dialog;
  272. this.progressBar = progressBar;
  273. this.progressText = progressText;
  274. this.statusText = statusText;
  275. this.installButton = installButton;
  276. // 显示进度条
  277. progressBar.setVisibility(View.VISIBLE);
  278. progressText.setVisibility(View.VISIBLE);
  279. statusText.setText("正在准备下载...");
  280. installButton.setEnabled(false);
  281. installButton.setText("下载中...");
  282. try {
  283. // 使用VersionUpdateUtil下载APK
  284. downloadId = updateUtil.downloadApk(updateInfo.getUpgradeurl(), updateInfo.getVersion());
  285. statusText.setText("开始下载,请勿关闭应用...");
  286. progressText.setText("下载已启动...");
  287. // 先检查是否已经有相同下载任务在进行或已完成
  288. if (isDownloadAlreadyExists(updateInfo.getUpgradeurl())) {
  289. Log.w(TAG, "检测到相同URL的下载任务已存在");
  290. }
  291. // 注册下载完成广播接收器
  292. IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
  293. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
  294. registerReceiver(downloadReceiver, filter, Context.RECEIVER_EXPORTED);
  295. } else {
  296. registerReceiver(downloadReceiver, filter);
  297. Log.d(TAG, "已注册广播接收器");
  298. }
  299. // 开始定期查询下载进度
  300. startProgressTracking();
  301. } catch (Exception e) {
  302. statusText.setText("下载失败: " + e.getMessage());
  303. installButton.setEnabled(true);
  304. installButton.setText("重试");
  305. installButton.setOnClickListener(v -> {
  306. startDownloadApk(updateInfo, dialog, progressBar, progressText, statusText, installButton);
  307. });
  308. }
  309. }
  310. // 添加检查下载任务是否已存在的方法
  311. private boolean isDownloadAlreadyExists(String downloadUrl) {
  312. DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
  313. DownloadManager.Query query = new DownloadManager.Query();
  314. Cursor cursor = downloadManager.query(query);
  315. if (cursor != null) {
  316. int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_URI);
  317. while (cursor.moveToNext()) {
  318. String uri = cursor.getString(uriIndex);
  319. if (downloadUrl.equals(uri)) {
  320. cursor.close();
  321. return true;
  322. }
  323. }
  324. cursor.close();
  325. }
  326. return false;
  327. }
  328. // 添加进度跟踪方法
  329. private void startProgressTracking() {
  330. isDownloading = true;
  331. progressRunnable = new Runnable() {
  332. @Override
  333. public void run() {
  334. if (isDownloading) {
  335. updateProgress();
  336. progressHandler.postDelayed(this, 1000); // 每秒更新一次
  337. }
  338. }
  339. };
  340. progressHandler.post(progressRunnable);
  341. }
  342. // 更新进度显示
  343. private void updateProgress() {
  344. DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
  345. DownloadManager.Query query = new DownloadManager.Query();
  346. query.setFilterById(downloadId);
  347. Cursor cursor = downloadManager.query(query);
  348. if (cursor != null && cursor.moveToFirst()) {
  349. int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
  350. int status = cursor.getInt(statusIndex);
  351. switch (status) {
  352. case DownloadManager.STATUS_RUNNING:
  353. int bytesDownloadedIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
  354. int bytesTotalIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
  355. long bytesDownloaded = cursor.getLong(bytesDownloadedIndex);
  356. long bytesTotal = cursor.getLong(bytesTotalIndex);
  357. if (bytesTotal > 0) {
  358. int progress = (int) ((bytesDownloaded * 100) / bytesTotal);
  359. progressBar.setProgress(progress);
  360. progressText.setText(String.format("下载中... %d%%", progress));
  361. statusText.setText("正在下载应用更新...");
  362. }
  363. break;
  364. case DownloadManager.STATUS_PENDING:
  365. statusText.setText("等待下载开始...");
  366. progressText.setText("准备中...");
  367. break;
  368. case DownloadManager.STATUS_PAUSED:
  369. statusText.setText("下载已暂停");
  370. progressText.setText("下载暂停");
  371. break;
  372. case DownloadManager.STATUS_FAILED:
  373. statusText.setText("下载失败");
  374. progressText.setText("下载失败,请重试");
  375. stopProgressTracking();
  376. // 显示重试按钮
  377. installButton.setEnabled(true);
  378. installButton.setText("重试");
  379. installButton.setOnClickListener(v -> {
  380. // 重新开始下载逻辑
  381. });
  382. break;
  383. }
  384. }
  385. if (cursor != null) {
  386. cursor.close();
  387. }
  388. }
  389. // 停止进度跟踪
  390. private void stopProgressTracking() {
  391. isDownloading = false;
  392. if (progressRunnable != null) {
  393. progressHandler.removeCallbacks(progressRunnable);
  394. }
  395. }
  396. // 修改 downloadReceiver 添加额外验证
  397. private final BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
  398. @Override
  399. public void onReceive(Context context, Intent intent) {
  400. Log.d(TAG, "收到广播: " + intent.getAction());
  401. if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
  402. long receivedDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
  403. Log.d(TAG, "下载ID: " + receivedDownloadId + ", 期望ID: " + downloadId);
  404. // 额外验证:确保是当前会话的下载任务
  405. if (receivedDownloadId == downloadId && downloadId != 0) {
  406. Log.d(TAG, "下载完成,准备更新UI");
  407. // 停止进度跟踪
  408. stopProgressTracking();
  409. // 下载完成,更新UI
  410. runOnUiThread(() -> {
  411. if (!isFinishing() && progressText != null && statusText != null && installButton != null) {
  412. progressText.setText("");
  413. statusText.setText("下载已完成,准备安装");
  414. progressBar.setProgress(100);
  415. installButton.setEnabled(true);
  416. installButton.setText("立即安装");
  417. installButton.setVisibility(View.VISIBLE);
  418. // 设置安装按钮点击事件
  419. installButton.setOnClickListener(installView -> {
  420. installDownloadedApk(downloadId, dialog);
  421. });
  422. Log.d(TAG, "UI更新完成");
  423. } else {
  424. Log.w(TAG, "Activity已结束或UI组件为空");
  425. }
  426. });
  427. } else {
  428. Log.d(TAG, "收到其他下载任务的完成广播或无效下载ID");
  429. }
  430. }
  431. }
  432. };
  433. // 在 onDestroy 中停止进度跟踪
  434. @Override
  435. protected void onDestroy() {
  436. super.onDestroy();
  437. // 停止进度跟踪
  438. stopProgressTracking();
  439. // 清理资源
  440. if (disposables != null && !disposables.isDisposed()) {
  441. disposables.dispose();
  442. }
  443. // 注销广播接收器
  444. try {
  445. unregisterReceiver(downloadReceiver);
  446. Log.d(TAG, "广播接收器已注销");
  447. } catch (IllegalArgumentException e) {
  448. // 接收器未注册,忽略
  449. Log.w(TAG, "广播接收器未注册或已注销: " + e.getMessage());
  450. }
  451. }
  452. private void installDownloadedApk(long downloadId, AlertDialog dialog) {
  453. try {
  454. // 通过DownloadManager查询下载的文件
  455. DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
  456. DownloadManager.Query query = new DownloadManager.Query();
  457. query.setFilterById(downloadId);
  458. Cursor cursor = downloadManager.query(query);
  459. if (cursor != null && cursor.moveToFirst()) {
  460. int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
  461. if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(statusIndex)) {
  462. // 获取下载文件的URI
  463. int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
  464. String downloadedFileUriString = cursor.getString(uriIndex);
  465. if (downloadedFileUriString != null) {
  466. Uri apkUri = Uri.parse(downloadedFileUriString);
  467. // Android 7.0及以上版本使用FileProvider处理file:// URI
  468. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && "file".equals(apkUri.getScheme())) {
  469. // 将 file:// URI 转换为 content:// URI
  470. File apkFile = new File(apkUri.getPath());
  471. if (apkFile.exists()) {
  472. Uri contentUri = androidx.core.content.FileProvider.getUriForFile(
  473. this,
  474. "com.paul.drone.fileprovider",
  475. apkFile
  476. );
  477. // 授予临时权限给包管理器
  478. updateUtil.installApk(contentUri);
  479. } else {
  480. Toast.makeText(this, "APK文件不存在", Toast.LENGTH_LONG).show();
  481. }
  482. } else {
  483. // Android 7.0以下版本或已经是content:// URI,直接使用
  484. updateUtil.installApk(apkUri);
  485. }
  486. dialog.dismiss();
  487. finish();
  488. } else {
  489. Toast.makeText(this, "无法找到下载的文件", Toast.LENGTH_LONG).show();
  490. }
  491. } else {
  492. Toast.makeText(this, "下载未完成", Toast.LENGTH_LONG).show();
  493. }
  494. } else {
  495. Toast.makeText(this, "下载查询失败", Toast.LENGTH_LONG).show();
  496. }
  497. if (cursor != null) {
  498. cursor.close();
  499. }
  500. } catch (Exception e) {
  501. Log.e(TAG, "安装APK失败: " + e.getMessage(), e);
  502. Toast.makeText(this, "安装失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
  503. }
  504. }
  505. }