package com.gh.gamecenter.manager; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.text.Html; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.ProgressBar; import android.widget.TextView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.app.NotificationManagerCompat; import com.gh.common.exposure.ExposureUtils; import com.gh.common.util.DialogUtils; import com.gh.common.util.DirectUtils; import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.download.DownloadManager; import com.gh.gamecenter.MainActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.constant.Constants; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.DataLogUtils; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.utils.NetworkUtils; import com.gh.gamecenter.common.utils.SensorsBridge; import com.gh.gamecenter.common.view.CustomLinkMovementMethod; import com.gh.gamecenter.core.AppExecutor; import com.gh.gamecenter.core.utils.CurrentActivityHolder; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.core.utils.EmptyCallback; import com.gh.gamecenter.core.utils.GsonUtils; import com.gh.gamecenter.core.utils.MD5Utils; import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.core.utils.SPUtils; import com.gh.gamecenter.core.utils.SpanBuilder; import com.gh.gamecenter.core.utils.SpeedUtils; import com.gh.gamecenter.entity.AppEntity; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.exposure.ExposureEvent; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.ndownload.NDataChanger; import com.halo.assistant.HaloApp; import com.lightgame.download.DataWatcher; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadStatus; import com.lightgame.download.FileUtils; import com.lightgame.utils.AppManager; import com.lightgame.utils.Utils; import java.io.File; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import retrofit2.HttpException; // 吐了,这个祖传的类,代码写得不是一般的糟糕,还不敢随便改 : ( /** * Created by LGT on 2016/12/8. * 助手更新 工具类 */ public class UpdateManager { // 用于控制第二次打开更新弹窗 private final static String ONCE_ONLY_SECOND_DEFAULT = "ONCE_ONLY_SECOND_DEFAULT"; private final static String ONCE_ONLY_SECOND_CLOSE = "ONCE_ONLY_SECOND_CLOSE"; private final static String ONCE_ONLY_SECOND_OPEN = "ONCE_ONLY_SECOND_OPEN"; private Context mContext; private Context mApplicationContext; private SharedPreferences mSp; private AppEntity appEntity; private EmptyCallback mOnDismissCallback; private Dialog downloadDialog; private Dialog loadingDialog; private Dialog updateDialog; private ProgressBar app_pb_progress; private TextView appProgressSize; private TextView appProgressRemain; private TextView appProgressPercent; private View appProgressFilling; private View appProgressAnchor; private TextView cancelUpdateTextView; private TextView downloadedHintView; private TextView confirmTextView; private boolean isShowDownload; private boolean isChecking; private long lastUpdateFileSize; private String currentUpdateMd5; private DataWatcher dataWatcher = new DataWatcher() { @Override public void onDataChanged(DownloadEntity downloadEntity) { if (downloadEntity.getName().contains("光环助手")) { if (downloadDialog != null && downloadDialog.isShowing() && isShowDownload) { // 被取消任务的状态不用来更新页面 if (!DownloadStatus.cancel.equals(downloadEntity.getStatus())) { float size = (((float) downloadEntity.getProgress() / 1024) / 1024); DecimalFormat df = new DecimalFormat("0.00"); appProgressSize.setText((df.format(size) + "MB")); appProgressRemain.setText(String.format("剩余%s", SpeedUtils.getRemainSecondTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024))); app_pb_progress.setProgress((int) (downloadEntity.getPercent() * 10)); int width = app_pb_progress.getWidth(); int marLeft = (int) (downloadEntity.getPercent() / 100 * width); ViewGroup.LayoutParams anchorLp = appProgressAnchor.getLayoutParams(); if (anchorLp instanceof ConstraintLayout.LayoutParams) { ((ConstraintLayout.LayoutParams) anchorLp).leftMargin = marLeft; appProgressAnchor.setLayoutParams(anchorLp); } if (downloadEntity.getSize() != lastUpdateFileSize) { lastUpdateFileSize = downloadEntity.getSize(); SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); } ViewGroup.LayoutParams fillingLp = appProgressFilling.getLayoutParams(); fillingLp.width = marLeft + DisplayUtils.dip2px(5); appProgressFilling.setLayoutParams(fillingLp); appProgressPercent.setText(((int) downloadEntity.getPercent() + "%")); } if (DownloadStatus.done.equals(downloadEntity.getStatus())) { DownloadManager.getInstance().cancel(downloadEntity.getUrl(), false, true, false); if (downloadDialog != null) { try { downloadDialog.dismiss(); } catch (IllegalArgumentException ignored) { // do nothing } } if (appEntity != null && appEntity.isForce()) { AppExecutor.getUiExecutor().executeWithDelay(() -> exitApp(), 1000); } } else if (DownloadStatus.neterror.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "网络错误,请稍后重试"); } else if (DownloadStatus.diskisfull.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "磁盘已满,请清理后重试"); } else if (DownloadStatus.diskioerror.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "磁盘 IO 异常,请稍后重试"); } else if (DownloadStatus.timeout.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "请求超时,请稍后重试"); } else if (DownloadStatus.notfound.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "下载链接异常,请稍后重试"); } else if (DownloadStatus.hijack.equals(downloadEntity.getStatus())) { Utils.toast(mApplicationContext, "网络劫持,请稍后重试"); } } else { if (downloadEntity.getSize() != lastUpdateFileSize) { lastUpdateFileSize = downloadEntity.getSize(); SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); } if (updateDialog != null && updateDialog.isShowing()) { // 强制更新的时候完成下载不用更新页面 if (DownloadStatus.done.equals(downloadEntity.getStatus()) && !appEntity.isForce()) { updateUpdateDialogView(true); } } else { if (mContext instanceof MainActivity && AppManager.getInstance().getRecentActiveActivity() == mContext) { if (DownloadStatus.done.equals(downloadEntity.getStatus())) { showUpdateDialog(currentUpdateMd5); } } } } } } }; private UpdateManager(Context context) { mContext = context; mApplicationContext = context.getApplicationContext(); mSp = PreferenceManager.getDefaultSharedPreferences(mApplicationContext); this.isShowDownload = false; this.isChecking = false; this.loadingDialog = null; lastUpdateFileSize = SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0L); } public static UpdateManager getInstance(Context context) { return new UpdateManager(context); } // 检查更新 public void checkUpdate(final boolean isAutoCheck, final Handler handler) { if (isChecking) { return; } isChecking = true; if (!isAutoCheck) { loadingDialog = DialogUtils.showWaitDialog(mContext, "检查更新中..."); } String channel = HaloApp.getInstance().getChannel(); RetrofitManager.getInstance().getApi().getUpdate(PackageUtils.getGhVersionName(), PackageUtils.getGhVersionCode(), channel) .map(appEntity -> { boolean isShowUpdateDialog = false; if (appEntity.getVersionCode() > PackageUtils.getGhVersionCode()) { // 助手有更新 UpdateManager.this.appEntity = appEntity; // 手动更新 强制更新 每次都提示 if (!isAutoCheck || "EVERY_TIME_OPEN".equals(appEntity.getAlert())) { isShowUpdateDialog = true; } else if ("ONCE_ONLY".equals(appEntity.getAlert())) { if (mSp.getBoolean(getUpdateOnceOnlySpKey(), true)) { isShowUpdateDialog = true; mSp.edit().putBoolean(getUpdateOnceOnlySpKey(), false).apply(); } } else if ("ONCE_ONLY_SECOND".equals(appEntity.getAlert())) { String onceOnlySecond = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT); if (ONCE_ONLY_SECOND_OPEN.equals(onceOnlySecond)) { isShowUpdateDialog = true; mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_CLOSE).apply(); } if (ONCE_ONLY_SECOND_DEFAULT.equals(onceOnlySecond)) { mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_OPEN).apply(); } } else if (!"NEVER".equals(appEntity.getAlert())) { // 一天提示一次 String showUpdateTime = mSp.getString("show_update_time", null); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); String today = format.format(new Date()); if (!today.equals(showUpdateTime)) { isShowUpdateDialog = true; mSp.edit().putString("show_update_time", today).apply(); } } } return isShowUpdateDialog ? MD5Utils.getUpdateMD5(appEntity.getUrl(), appEntity.getContent()) : null; }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Response() { @Override public void onResponse(String response) { isChecking = false; currentUpdateMd5 = response; if (loadingDialog != null) { loadingDialog.dismiss(); } if (response != null) { if (!appEntity.isForce() && isAutoCheck && !isUpdateFileDownloaded(response) && NetworkUtils.isWifiConnected(mApplicationContext)) { createUpdate(response, true); } else { showUpdateDialog(response); } if (handler != null) { Message message = new Message(); message.what = 0; message.obj = appEntity.getVersion(); handler.sendMessage(message); } } else if (!isAutoCheck) { Utils.toast(mApplicationContext, "已是最新版本"); if (handler != null) { handler.sendEmptyMessage(1); } invokeDismissCallback(); } } @Override public void onFailure(HttpException e) { isChecking = false; if (loadingDialog != null) { loadingDialog.dismiss(); } if (!isAutoCheck) { if (handler != null) { handler.sendEmptyMessage(1); } if (e != null && (e.code() == 304 || e.code() == 404)) { Utils.toast(mApplicationContext, "您的光环助手已是最新版本"); return; } Utils.toast(mApplicationContext, "检查更新失败"); } invokeDismissCallback(); } }); } // 显示助手有更新提示框 private void showUpdateDialog(final String md5) { Context context = getValidContext(); AtomicBoolean dismissByTouchInside = new AtomicBoolean(false); updateDialog = new Dialog(context); updateDialog.setOnDismissListener(dialog -> { invokeDismissCallback(); if (!isShowDownload) { DownloadManager.getInstance().removeObserver(dataWatcher); } if (!dismissByTouchInside.get()) { SensorsBridge.trackVersionUpdateDialogClick("关闭弹窗"); } }); Window window = updateDialog.getWindow(); if (window != null) { window.setBackgroundDrawableResource(android.R.color.transparent); } View view = View.inflate(context, R.layout.app_update_hint_dialog, null); cancelUpdateTextView = view.findViewById(R.id.cancel); downloadedHintView = view.findViewById(R.id.downloadedHint); confirmTextView = view.findViewById(R.id.confirm); if (!TextUtils.isEmpty(appEntity.getSpareLink())) { String defaultText = "部分设备若无法更新安装,请前往官网进行下载安装:"; TextView externalTextTv = view.findViewById(R.id.externalTextTv); SpannableStringBuilder builder = new SpanBuilder(defaultText + appEntity.getSpareLink()) .click(mContext, defaultText.length(), defaultText.length() + appEntity.getSpareLink().length(), R.color.theme_font, false, () -> { DirectUtils.directToExternalBrowser(context, appEntity.getSpareLink()); return null; }) .build(); externalTextTv.setText(builder); externalTextTv.setMovementMethod(CustomLinkMovementMethod.getInstance()); externalTextTv.setVisibility(View.VISIBLE); } if (NetworkUtils.isWifiConnected(context)) { if (!isUpdateFileDownloaded(md5)) { updateUpdateDialogView(false); createUpdate(md5, true); } else { updateUpdateDialogView(true); } } else { updateUpdateDialogView(isUpdateFileDownloaded(md5)); } TextView content = view.findViewById(R.id.desc); content.setText(Html.fromHtml(appEntity.getContent())); TextView version = view.findViewById(R.id.version); version.setText(String.format("版本%s更新日志:", appEntity.getVersion())); TextView size = view.findViewById(R.id.size); size.setText(String.format("大小 %s", appEntity.getSize())); view.setOnClickListener(v -> { if (appEntity.isForce()) { exitApp(); } else { updateDialog.dismiss(); } }); cancelUpdateTextView.setOnClickListener(v -> { if (appEntity.isForce()) { exitApp(); } else { updateDialog.dismiss(); } dismissByTouchInside.set(true); String cancelText = cancelUpdateTextView.getText().toString(); SensorsBridge.trackVersionUpdateDialogClick(cancelText); }); confirmTextView.setOnClickListener(v -> { if (!isUpdateFileDownloaded(md5)) { dismissByTouchInside.set(true); updateDialog.dismiss(); } else if (isUpdateFileDownloaded(md5) && !appEntity.isForce()) { dismissByTouchInside.set(true); updateDialog.dismiss(); } String path = FileUtils.getDownloadPath(context, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); // 产品不接受显示下载完成文案从立即更新变为立即安装,所以文案为立即更新时一律不执行安装 if (isUpdateFileDownloaded(md5) && confirmTextView.getText() != "立即更新") { DataLogUtils.uploadUpgradeLog(context, "install"); //上传更新安装数据 PackageInstaller.install(context, false, path, null); } else { MtaHelper.onEvent("软件更新", "下载开始"); showDownloadDialog(md5); } String buttonName = confirmTextView.getText().toString(); SensorsBridge.trackVersionUpdateDialogClick(buttonName); }); if (appEntity.isForce()) { updateDialog.setCanceledOnTouchOutside(false); updateDialog.setCancelable(false); } updateDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); updateDialog.setContentView(view); updateDialog.show(); SensorsBridge.trackVersionUpdateDialogShow(); DataLogUtils.uploadUpgradeLog(context, "notice"); //上传更新通知弹窗数据 } private void exitApp() { NotificationManagerCompat.from(mApplicationContext).cancelAll(); AppManager.getInstance().finishAllActivity(); } private void showDownloadDialog(String md5) { Context context = getValidContext(); if (NetworkUtils.isMobileConnected(context)) { Utils.toast(context, "当前使用移动数据进行下载"); } downloadDialog = new Dialog(context); downloadDialog.setOnDismissListener(dialog -> { invokeDismissCallback(); }); Window window = downloadDialog.getWindow(); if (window != null) { window.setBackgroundDrawableResource(android.R.color.transparent); } View view = View.inflate(context, R.layout.app_updating_dialog, null); app_pb_progress = view.findViewById(R.id.progress); appProgressSize = view.findViewById(R.id.size); appProgressRemain = view.findViewById(R.id.remain); appProgressAnchor = view.findViewById(R.id.progress_anchor); appProgressPercent = view.findViewById(R.id.percent); appProgressFilling = view.findViewById(R.id.progress_filling); view.findViewById(R.id.app_tv_cancel).setOnClickListener(v -> { DownloadManager.getInstance().cancel(appEntity.getUrl()); if (appEntity.isForce()) { exitApp(); } else { downloadDialog.dismiss(); } }); downloadDialog.setOnDismissListener(dialog -> { DownloadManager.getInstance().removeObserver(dataWatcher); isShowDownload = false; }); int dialogWidth = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60); downloadDialog.setCanceledOnTouchOutside(false); downloadDialog.setCancelable(false); downloadDialog.closeOptionsMenu(); downloadDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); downloadDialog.setContentView(view, new ViewGroup.LayoutParams(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); isShowDownload = true; createUpdate(md5, false); downloadDialog.show(); } private void createUpdate(String md5, boolean isSilentUpdate) { DownloadManager.getInstance().addObserver(dataWatcher); boolean shouldCancelPreviousDownload = true; // 是否应该取消旧更新任务 // 在部分设备上取消正在进行中的旧下载任务再创建新下载任务有机率造成新下载任务依然带有 "静默更新" 标签导致无法唤起安装 // 所以这里对当前静默更新任务正在进行中的时候就不用删旧任务,直接改 meta 标签 if (!isSilentUpdate && DownloadManager.getInstance().isTaskDownloading(appEntity.getUrl())) { try { DownloadEntity entity = NDataChanger.INSTANCE.getDownloadEntries().get(appEntity.getUrl()); if (entity != null) { ExtensionsKt.addMetaExtra(entity, Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新"); DownloadManager.getInstance().updateDownloadEntity(entity); DownloadManager.getInstance().resume(entity, false); shouldCancelPreviousDownload = false; } } catch (Exception e) { // 出现异常走删旧下载任务重下流程 shouldCancelPreviousDownload = true; e.printStackTrace(); } } // 预下载完成或者还没进行预下载的都进这里,先删掉旧的下载文件再进行下载 if (shouldCancelPreviousDownload) { String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); File file = new File(path); if (file.exists() && file.delete()) { Utils.log(file.getName() + " file delete success."); } ExposureEvent event = ExposureUtils.logADownloadExposureEvent( new GameEntity(Constants.GHZS_GAME_ID, "光环助手V" + appEntity.getVersion()), null, null, ExposureUtils.DownloadType.DOWNLOAD); DownloadEntity downloadEntity = new DownloadEntity(); downloadEntity.setUrl(appEntity.getUrl()); downloadEntity.setName("光环助手V" + appEntity.getVersion()); downloadEntity.setPath(path); downloadEntity.setPlatform("官方版"); downloadEntity.setGameId(Constants.GHZS_GAME_ID); downloadEntity.setExposureTrace(GsonUtils.toJson(event)); if (isSilentUpdate) { ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SILENT_UPDATE); } downloadEntity.setPackageName(mApplicationContext.getPackageName()); DownloadManager.getInstance().cancel(appEntity.getUrl(), true, true, false); DownloadManager.getInstance().pauseAll(); AppExecutor.getUiExecutor().executeWithDelay(() -> { DownloadManager.getInstance().add(downloadEntity); }, 200); } } /** * 尽量获取有效的 activity context */ private Context getValidContext() { Context context = mContext; Activity currentActivity = CurrentActivityHolder.getCurrentActivity(); if (currentActivity != null && mContext != currentActivity && !currentActivity.isFinishing()) { context = currentActivity; } return context; } private boolean isUpdateFileDownloaded(String md5) { String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); File file = new File(path); return file.exists() && file.length() == SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0); } private void updateUpdateDialogView(boolean isUpdateDownloaded) { if (isUpdateDownloaded) { confirmTextView.setText("立即安装"); downloadedHintView.setVisibility(View.VISIBLE); if (appEntity.isForce()) { cancelUpdateTextView.setText("暂不安装,退出光环"); } else { cancelUpdateTextView.setText("暂不安装"); } } else { confirmTextView.setText("立即更新"); downloadedHintView.setVisibility(View.GONE); if (appEntity.isForce()) { cancelUpdateTextView.setText("暂不更新,退出光环"); } else { cancelUpdateTextView.setText("暂不更新"); } } } public void setDismissCallback(EmptyCallback callback) { mOnDismissCallback = callback; } private void invokeDismissCallback() { if (mOnDismissCallback != null && (appEntity == null || !appEntity.isForce())) { mOnDismissCallback.onCallback(); } } private String getUpdateOnceOnlySpKey() { return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode(); } private String getUpdateOnceOnlySecondSpKey() { return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode(); } }