b82347e894b021c9d19957d3f091bb6988531cf5.svn-base 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. package eVVM.apk.widget;
  2. import android.app.DownloadManager;
  3. import android.content.Context;
  4. import android.content.Intent;
  5. import android.database.ContentObserver;
  6. import android.database.Cursor;
  7. import android.net.Uri;
  8. import android.os.Build;
  9. import android.os.Environment;
  10. import android.os.Handler;
  11. import android.os.Looper;
  12. import android.os.Message;
  13. import android.support.v4.app.FragmentActivity;
  14. import android.support.v4.content.FileProvider;
  15. import android.text.format.Formatter;
  16. import android.view.Gravity;
  17. import android.view.View;
  18. import android.view.ViewGroup;
  19. import android.widget.TextView;
  20. import com.hjq.base.BaseDialog;
  21. import com.hjq.base.BaseDialogFragment;
  22. import com.hjq.permissions.OnPermission;
  23. import com.hjq.permissions.Permission;
  24. import com.hjq.permissions.XXPermissions;
  25. import com.hjq.toast.ToastUtils;
  26. import com.hjq.widget.NumberProgressBar;
  27. import java.io.File;
  28. import java.util.List;
  29. import eVVM.apk.R;
  30. /**
  31. * Created by Android Studio.
  32. * User: zbb
  33. * Date: 2019/8/7
  34. * Describe: UpdateDialog
  35. */
  36. public class UpdateDialog {
  37. public static final class Builder
  38. extends BaseDialogFragment.Builder<Builder>
  39. implements OnDownloadListener,
  40. View.OnClickListener, OnPermission {
  41. private TextView mNameView;
  42. private TextView mSizeView;
  43. private TextView mContentView;
  44. private NumberProgressBar mProgressView;
  45. private TextView mUpdateView;
  46. private ViewGroup mCancelLayout;
  47. private View mCloseView;
  48. // 下载地址
  49. private String mDownloadUrl;
  50. // 当前下载状态
  51. private int mDownloadStatus = -1;
  52. // 下载处理对象
  53. private DownloadHandler mDownloadHandler;
  54. public Builder(FragmentActivity activity) {
  55. super(activity);
  56. setContentView(R.layout.dialog_update);
  57. setAnimStyle(BaseDialog.AnimStyle.TOAST);
  58. setGravity(Gravity.CENTER);
  59. mNameView = findViewById(R.id.tv_dialog_update_name);
  60. mSizeView = findViewById(R.id.tv_dialog_update_size);
  61. mContentView = findViewById(R.id.tv_dialog_update_content);
  62. mProgressView = findViewById(R.id.pb_dialog_update_progress);
  63. mUpdateView = findViewById(R.id.tv_dialog_update_update);
  64. mCancelLayout = findViewById(R.id.ll_dialog_update_cancel);
  65. mCloseView = findViewById(R.id.iv_dialog_update_close);
  66. mUpdateView.setOnClickListener(this);
  67. mCloseView.setOnClickListener(this);
  68. }
  69. /**
  70. * 设置版本名
  71. */
  72. public Builder setVersionName(CharSequence name) {
  73. mNameView.setText(name);
  74. return this;
  75. }
  76. /**
  77. * 设置文件大小
  78. */
  79. public Builder setFileSize(long size) {
  80. return setFileSize(Formatter.formatFileSize(getContext(), size));
  81. }
  82. public Builder setFileSize(CharSequence text) {
  83. mSizeView.setText(text);
  84. return this;
  85. }
  86. /**
  87. * 设置更新日志
  88. */
  89. public Builder setUpdateLog(CharSequence text) {
  90. mContentView.setText(text);
  91. mContentView.setVisibility(text == null ? View.GONE : View.VISIBLE);
  92. return this;
  93. }
  94. /**
  95. * 设置强制更新
  96. */
  97. public Builder setForceUpdate(boolean force) {
  98. mCancelLayout.setVisibility(force ? View.GONE : View.VISIBLE);
  99. return setCancelable(!force);
  100. }
  101. /**
  102. * 设置下载 url
  103. */
  104. public Builder setDownloadUrl(String url) {
  105. mDownloadUrl = url;
  106. return this;
  107. }
  108. /**
  109. * {@link OnDownloadListener}
  110. */
  111. @Override
  112. public void downloadProgressChange(int progress) {
  113. mProgressView.setProgress(progress);
  114. }
  115. @Override
  116. public void downloadStateChange(int state) {
  117. // 记录本次的下载状态
  118. mDownloadStatus = state;
  119. // 判断下载状态
  120. switch (state) {
  121. case DownloadManager.STATUS_RUNNING: // 下载中
  122. mUpdateView.setText(R.string.dialog_update_status_running);
  123. // 显示进度条
  124. mProgressView.setVisibility(View.VISIBLE);
  125. break;
  126. case DownloadManager.STATUS_SUCCESSFUL: // 下载成功
  127. mUpdateView.setText(R.string.dialog_update_status_successful);
  128. // 隐藏进度条
  129. mProgressView.setVisibility(View.GONE);
  130. // 安装 Apk
  131. mDownloadHandler.openDownloadFile();
  132. break;
  133. case DownloadManager.STATUS_FAILED: // 下载失败
  134. mUpdateView.setText(R.string.dialog_update_status_failed);
  135. // 删除下载的文件
  136. mDownloadHandler.deleteDownloadFile();
  137. break;
  138. case DownloadManager.STATUS_PAUSED: // 下载暂停
  139. mUpdateView.setText(R.string.dialog_update_status_paused);
  140. break;
  141. case DownloadManager.STATUS_PENDING: // 等待下载
  142. mUpdateView.setText(R.string.dialog_update_status_pending);
  143. break;
  144. default:
  145. break;
  146. }
  147. }
  148. /**
  149. * {@link View.OnClickListener,}
  150. */
  151. @Override
  152. public void onClick(View v) {
  153. if (v == mCloseView) { // 点击了下次再说
  154. dismiss();
  155. }else if (v == mUpdateView) { // 点击了更新按钮
  156. // 判断下载状态
  157. switch (mDownloadStatus) {
  158. case -1: // 没有任何状态
  159. case DownloadManager.STATUS_FAILED: // 下载失败
  160. // 重新下载
  161. requestPermission();
  162. break;
  163. case DownloadManager.STATUS_SUCCESSFUL: // 下载成功
  164. // 安装 Apk
  165. mDownloadHandler.openDownloadFile();
  166. break;
  167. default:
  168. break;
  169. }
  170. }
  171. }
  172. /**
  173. * 请求权限
  174. */
  175. private void requestPermission() {
  176. XXPermissions.with(getActivity())
  177. .constantRequest() // 可设置被拒绝后继续申请,直到用户授权或者永久拒绝
  178. .permission(Permission.REQUEST_INSTALL_PACKAGES) //安装包权限
  179. .permission(Permission.Group.STORAGE) // 存储权限
  180. .request(this);
  181. }
  182. /**
  183. * {@link OnPermission}
  184. */
  185. @Override
  186. public void hasPermission(List<String> granted, boolean isAll) {
  187. if (isAll) {
  188. mDownloadHandler = new DownloadHandler(getActivity());
  189. mDownloadHandler.setDownloadListener(this);
  190. if (!mDownloadHandler.createDownload(mDownloadUrl, getString(R.string.app_name) +
  191. " " + mNameView.getText().toString() + ".apk", null)) {
  192. mUpdateView.setText(R.string.dialog_update_download_fail);
  193. } else {
  194. // 设置对话框不能被取消
  195. setCancelable(false);
  196. // 隐藏取消按钮
  197. mCancelLayout.setVisibility(View.GONE);
  198. }
  199. }
  200. }
  201. @Override
  202. public void noPermission(List<String> denied, boolean quick) {
  203. ToastUtils.show(R.string.dialog_update_permission_hint);
  204. }
  205. }
  206. private static final class DownloadHandler extends Handler {
  207. private Context mContext;
  208. private DownloadManager mDownloadManager; // 下载管理器对象
  209. private DownloadObserver mDownloadObserver; // 下载内容观察者
  210. private long mDownloadId; // 下载 id
  211. private OnDownloadListener mListener; // 下载监听
  212. private File mDownloadFile; // 下载的文件
  213. private DownloadHandler(Context context) {
  214. super(Looper.getMainLooper());
  215. mContext = context;
  216. mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
  217. }
  218. private void setDownloadListener(OnDownloadListener l) {
  219. this.mListener = l;
  220. }
  221. @Override
  222. public void handleMessage(Message msg) {
  223. if (mListener == null) return;
  224. // 判断下载状态
  225. switch (msg.what) {
  226. case DownloadManager.STATUS_RUNNING: // 下载中
  227. // 计算下载百分比
  228. int progress = (int) (new Integer(msg.arg2).longValue() * 100 / new Integer(msg.arg1).longValue());
  229. mListener.downloadProgressChange(progress);
  230. break;
  231. case DownloadManager.STATUS_SUCCESSFUL: // 下载成功
  232. case DownloadManager.STATUS_FAILED: // 下载失败
  233. // 移除内容观察者
  234. if (mDownloadObserver != null) {
  235. mContext.getContentResolver().unregisterContentObserver(mDownloadObserver);
  236. }
  237. break;
  238. default:
  239. break;
  240. }
  241. mListener.downloadStateChange(msg.what);
  242. }
  243. /**
  244. * 创建下载任务
  245. *
  246. * @param downloadUrl 下载地址
  247. * @param fileName 文件命名
  248. * @param notificationTitle 通知栏标题
  249. * @return 下载 id
  250. */
  251. private boolean createDownload(String downloadUrl, String fileName, String notificationTitle) {
  252. if (fileName == null) {
  253. throw new IllegalArgumentException("The filename cannot be empty");
  254. }
  255. // 记录下载的文件
  256. mDownloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
  257. // 如果这个文件已经下载过,就先删除这个文件
  258. if (mDownloadFile.exists()) {
  259. mDownloadFile.delete();
  260. }
  261. try {
  262. DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
  263. request.allowScanningByMediaScanner();
  264. //设置WIFI下进行更新
  265. //request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
  266. request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  267. request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
  268. if (notificationTitle != null) {
  269. request.setTitle(notificationTitle);
  270. }
  271. request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  272. mDownloadId = mDownloadManager.enqueue(request);
  273. mDownloadObserver = new DownloadObserver(this, mDownloadManager, new DownloadManager.Query().setFilterById(mDownloadId));
  274. // 添加内容观察者
  275. mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/"), true, mDownloadObserver);
  276. return true;
  277. } catch (Exception e) {
  278. return false;
  279. }
  280. }
  281. /**
  282. * 打开下载的文件
  283. */
  284. private void openDownloadFile() {
  285. // 这里需要特别说明的是,这个 API 其实不是打开文件的,我也不知道干什么用的
  286. // 测试前必须要加权限,否则会崩溃:<uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS" />
  287. // mDownloadManager.openDownloadedFile(mDownloadId);
  288. Intent intent = new Intent();
  289. intent.setAction(Intent.ACTION_VIEW);
  290. Uri uri;
  291. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  292. uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", mDownloadFile);
  293. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  294. } else {
  295. uri = Uri.fromFile(mDownloadFile);
  296. }
  297. intent.setDataAndType(uri, "application/vnd.android.package-archive");
  298. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  299. mContext.startActivity(intent);
  300. }
  301. /**
  302. * 删除下载的文件
  303. */
  304. void deleteDownloadFile() {
  305. mDownloadManager.remove(mDownloadId);
  306. }
  307. }
  308. private static class DownloadObserver extends ContentObserver {
  309. private Handler mHandler;
  310. private DownloadManager mDownloadManager;
  311. private DownloadManager.Query mQuery;
  312. DownloadObserver(Handler handler, DownloadManager manager, DownloadManager.Query query) {
  313. super(handler);
  314. mHandler = handler;
  315. mDownloadManager = manager;
  316. mQuery = query;
  317. }
  318. /**
  319. * 每当 /data/data/com.android.providers.download/database/database.db 变化后就会触发onChange方法
  320. *
  321. * @param selfChange 是否是当前应用自己操作了数据库
  322. */
  323. @Override
  324. public void onChange(boolean selfChange) {
  325. // 查询数据库
  326. Cursor cursor = mDownloadManager.query(mQuery);
  327. // 游标定位到第一个,因为 Cursor 总数只有一个
  328. cursor.moveToFirst();
  329. // 总需下载的字节数
  330. int totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
  331. // 已经下载的字节数
  332. int downloadedBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
  333. // 下载状态
  334. int downloadStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
  335. // 关闭游标
  336. cursor.close();
  337. // 发送更新消息
  338. Message msg = mHandler.obtainMessage();
  339. msg.arg1 = totalBytes;
  340. msg.arg2 = downloadedBytes;
  341. msg.what = downloadStatus;
  342. mHandler.sendMessage(msg);
  343. }
  344. }
  345. private interface OnDownloadListener {
  346. /**
  347. * 下载进度改变
  348. */
  349. void downloadProgressChange(int progress);
  350. /**
  351. * 下载状态改变
  352. */
  353. void downloadStateChange(int state);
  354. }
  355. }