package com.gh.download; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArrayMap; import com.gh.common.AppExecutor; import com.gh.common.constant.Constants; import com.gh.common.exposure.ExposureEvent; import com.gh.common.exposure.ExposureUtils; import com.gh.common.exposure.meta.MetaUtil; import com.gh.common.history.HistoryHelper; import com.gh.common.simulator.SimulatorGameManager; import com.gh.common.util.AppDebugConfig; import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DeviceUtils; import com.gh.common.util.DialogUtils; import com.gh.common.util.ExtensionsKt; import com.gh.common.util.GsonUtils; import com.gh.common.util.HomePluggableHelper; import com.gh.common.util.NetworkUtils; import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PageSwitchDataHelper; import com.gh.common.util.SPUtils; import com.gh.gamecenter.BuildConfig; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.GameUpdateEntity; import com.gh.gamecenter.entity.HomePluggableFilterEntity; import com.gh.gamecenter.entity.PluginLocation; import com.gh.gamecenter.eventbus.EBDownloadStatus; import com.gh.gamecenter.manager.PackagesManager; import com.gh.gamecenter.manager.UserManager; import com.gh.gamecenter.packagehelper.PackageRepository; import com.google.gson.Gson; import com.halo.assistant.HaloApp; import com.lightgame.config.CommonDebug; import com.lightgame.download.ConnectionUtils; import com.lightgame.download.DataChanger; import com.lightgame.download.DataWatcher; import com.lightgame.download.DownloadConfig; import com.lightgame.download.DownloadDao; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadService; import com.lightgame.download.DownloadStatus; import com.lightgame.download.DownloadStatusListener; import com.lightgame.download.DownloadStatusManager; import com.lightgame.download.DownloadTask; import com.lightgame.download.FileUtils; import com.lightgame.download.HttpDnsManager; import com.lightgame.utils.Utils; import org.greenrobot.eventbus.EventBus; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; public class DownloadManager implements DownloadStatusListener { private static DownloadManager mInstance; private static Gson gson = new Gson(); private static final String UPDATE_IS_READ_MARK = "update_is_read"; private static final String DOWNLOADING_IS_READ_MARK = "downloading_is_read"; private static final String DOWNLOADED_IS_READ_MARK = "downloaded_is_read"; private Context mContext; private Handler mHandler; private ArrayMap lastTimeMap; private ArrayMap> platformMap; private ArrayMap> gameMap; private ArrayMap statusMap; private ArrayMap downloadingMap; private DownloadDao mDownloadDao; private Set mUpdateMarks; @Override public void onTaskCancelled(DownloadEntity entity) { EBDownloadStatus status = new EBDownloadStatus("delete", entity.getName(), entity.getPlatform(), entity.getUrl(), entity.getPackageName(), entity.getGameId()); status.setPluggable(entity.isPluggable()); EventBus.getDefault().post(status); DownloadManager.getInstance(mContext).putStatus(entity.getUrl(), DownloadStatus.delete); downloadingMap.remove(entity.getUrl()); } @Override public void onTaskAdded(DownloadEntity entity) { EventBus.getDefault().post(new EBDownloadStatus("download", entity.getName(), entity.getPlatform(), entity.getUrl(), entity.getPackageName(), entity.getGameId())); downloadingMap.put(entity.getUrl(), entity); DownloadWorkManager.addWorker(); } @Override public void onTaskError(DownloadEntity entity) { // 下载进度超出是任务出错,但不需要去掉状态栏通知 https://gitlab.ghzs.com/pm/halo-app-issues/issues/496 if (entity.getStatus() == DownloadStatus.overflow) { // MtaHelper.onEventWithBasicDeviceInfo("下载无法完成", "游戏", entity.getName()); } else { downloadingMap.remove(entity.getUrl()); } } @Override public void onTaskStatusChanged(DownloadEntity entity) { } @Override public void onTaskDone(DownloadEntity entity) { downloadingMap.remove(entity.getUrl()); if (downloadingMap.isEmpty()) { DownloadWorkManager.cancelWorker(); } } @Override public void onTaskPaused(DownloadEntity entity) { } private DownloadManager(Context context) { mContext = context.getApplicationContext(); mDownloadDao = DownloadDao.getInstance(mContext); mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK); DownloadStatusManager.getInstance().registerTaskStatusListener(this); // 只有下载模块需要这坨东西,因此移动到这里初始化 ConnectionUtils.initHttpsUrlConnection(context); updateMetaMap(); // DownloadNotification.showDownloadingNotification(mContext); lastTimeMap = new ArrayMap<>(); platformMap = new ArrayMap<>(); gameMap = new ArrayMap<>(); statusMap = new ArrayMap<>(); downloadingMap = new ArrayMap<>(); mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { String url = (String) msg.obj; switch (msg.what) { case DownloadConfig.CONTINUE_DOWNLOAD_TASK: case DownloadConfig.CONTINUE_DOWNLOAD_AUTO_TASK: if (lastTimeMap.get(url) != null && System.currentTimeMillis() - lastTimeMap.get(url) >= 1000) { DownloadEntity downloadEntity = getDownloadEntityByUrl(url); if (downloadEntity != null) { resume(downloadEntity, msg.what == DownloadConfig.CONTINUE_DOWNLOAD_AUTO_TASK); } } break; case DownloadConfig.PAUSE_DOWNLOAD_TASK: if (lastTimeMap.get(url) != null && System.currentTimeMillis() - lastTimeMap.get(url) >= 1000) { pause(url); } break; case DownloadConfig.DOWNLOAD_ROLL: LinkedBlockingQueue queue = platformMap.get(url); if (queue.size() > 1) { queue.offer(queue.poll()); Message message = Message.obtain(); message.obj = url; message.what = DownloadConfig.DOWNLOAD_ROLL; sendMessageDelayed(message, 3000); } break; } } }; List list = getAllDownloadEntity(); for (DownloadEntity downloadEntity : list) { statusMap.put(downloadEntity.getUrl(), downloadEntity.getStatus()); if (!DownloadStatus.done.equals(downloadEntity.getStatus())) { downloadingMap.put(downloadEntity.getUrl(), downloadEntity); } } } public ArrayMap getDownloadingMap() { return downloadingMap; } public static DownloadManager getInstance(Context context) { if (mInstance == null) { synchronized (DownloadManager.class) { if (mInstance == null) { mInstance = new DownloadManager(context.getApplicationContext()); } } } return mInstance; } public static void createDownload(Context context, GameEntity gameEntity, String method, String entrance, String location, boolean isSubscribe, @Nullable ExposureEvent traceEvent) { createDownload(context, gameEntity.getApk().get(0), gameEntity, method, entrance, location, isSubscribe, traceEvent); } public static void createDownload(final Context context, ApkEntity apkEntity, GameEntity gameEntity, String method, String entrance, String location, boolean isSubscribe, @Nullable ExposureEvent traceEvent) { // 插件版本下载互斥弹窗 List mutexPackage = gameEntity.getMutexPackage(); if (mutexPackage != null && mutexPackage.size() > 0) { for (String pkg : mutexPackage) { if (PackagesManager.isInstalled(pkg)) { DialogUtils.showDownloadMutexDialog(context); break; } } } String path; String downloadId = PackageInstaller.createDownloadId(gameEntity.getName()); if (SimulatorGameManager.isSimulatorGame(gameEntity)) { path = SimulatorGameManager.getPathByType(gameEntity.getSimulatorType()) + "/" + gameEntity.getName() + "." + apkEntity.getFormat(); // 下载模拟器游戏配置文件,地址是 "模拟器游戏类型根目录/cheat/" if (!TextUtils.isEmpty(gameEntity.getSimulatorGameConfig())) { String configFilePath = SimulatorGameManager.getPathByType(gameEntity.getSimulatorType()) + "/cheat/" + gameEntity.getName() + ".ini"; AppExecutor.getIoExecutor().execute(() -> { FileUtils.downloadFile(gameEntity.getSimulatorGameConfig(), configFilePath); }); } } else { path = PackageInstaller.getDownloadPathWithId(downloadId, apkEntity.getFormat()); } File file = new File(path); DownloadEntity entity = DownloadDao.getInstance(context).get(apkEntity.getUrl()); //判断当前链接没有下载记录并且文件已经存在则删除这个文件 if (entity == null && file.exists()) { file.delete(); } DownloadEntity downloadEntity = new DownloadEntity(); downloadEntity.setUrl(apkEntity.getUrl()); downloadEntity.setName(gameEntity.getName()); downloadEntity.setPath(path); downloadEntity.setETag(apkEntity.getEtag()); downloadEntity.setIcon(gameEntity.getIcon()); downloadEntity.setPlatform(apkEntity.getPlatform()); downloadEntity.setPackageName(apkEntity.getPackageName()); downloadEntity.setGameId(gameEntity.getId()); downloadEntity.setEntrance(entrance); downloadEntity.setLocation(location); downloadEntity.setVersionName(apkEntity.getVersion()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.DOWNLOAD_ID, downloadId); ExtensionsKt.addMetaExtra(downloadEntity, Constants.RAW_GAME_ICON, gameEntity.getRawIcon()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_SUBSCRIPT, gameEntity.getIconSubscript()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.IS_PLATFORM_RECOMMEND, apkEntity.getRecommend() != null ? "true" : "false"); ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_NAME, gameEntity.getName()); if (SimulatorGameManager.isSimulatorGame(gameEntity)) { ExtensionsKt.addMetaExtra(downloadEntity, Constants.SIMULATOR_GAME, apkEntity.getFormat()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.SIMULATOR, GsonUtils.toJson(gameEntity.getSimulator())); } HashMap map = PageSwitchDataHelper.popLastPageData(); if (map != null && map.containsKey(PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND)) { ExtensionsKt.addMetaExtra(downloadEntity, PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND, "true"); } int installed = 0; for (ApkEntity apk : gameEntity.getApk()) { if (PackagesManager.isInstalled(apk.getPackageName())) { installed++; } } downloadEntity.setInstalled(installed); // todo 不是应该实时判断吗? if (PackageUtils.isCanPluggable(apkEntity)) { downloadEntity.setPluggable(true); } else if (PackageUtils.isCanUpdate(apkEntity, gameEntity.getId()) || PackageUtils.isNonPluginUpdatable(apkEntity, gameEntity)) { downloadEntity.setUpdate(true); } downloadEntity.setPlugin(!TextUtils.isEmpty(apkEntity.getGhVersion())); ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, gameEntity.getId()); gameEntity.setIsPlatformRecommend(apkEntity.getRecommend() != null); ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity.getPlatform(), traceEvent, downloadType); // 将下载事件放入 downloadEntity 中供下载完成时取出使用 downloadEntity.setExposureTrace(gson.toJson(downloadExposureEvent)); if (isSubscribe) { DownloadManager.getInstance(context).subscribe(downloadEntity); } else { HistoryHelper.insertGameEntity(gameEntity); DownloadManager.getInstance(context).add(downloadEntity); } if (AppDebugConfig.IS_DEBUG) { AppDebugConfig.logMethodWithParams(DownloadManager.class, apkEntity.getUrl(), downloadEntity.getUrl(), method, entrance, location); } //TODO remove DownloadManager.getInstance(context).putStatus(downloadEntity.getUrl(), DownloadStatus.downloading); DownloadManager.getInstance(context).markDownloadingTaskAsUnread(); // 收集下载数据 DataCollectionUtils.uploadDownload(context, downloadEntity, "开始"); // GdtHelper.INSTANCE.logAction(ActionType.DOWNLOAD_APP, // GdtHelper.GAME_ID, downloadEntity.getGameId(), // GdtHelper.PLATFORM, downloadEntity.getPlatform()); } public void removeAllTaskThatFileDoesNotExist() { for (DownloadEntity entity : getAllDownloadEntity()) { if (entity.getStatus() == DownloadStatus.done) { if (FileUtils.isEmptyFile(entity.getPath())) { cancel(entity.getUrl()); } } } } /** * 添加一个下载任务 * * @param downloadEntity */ public void add(DownloadEntity downloadEntity) { updateMetaMap(); if (downloadEntity != null) { String url = downloadEntity.getUrl(); checkDownloadEntryRecordValidate(url); if (isFileCompleted(url)) { downloadEntity.setStatus(DownloadStatus.done); DataChanger.INSTANCE.notifyDataChanged(downloadEntity); } else if (!isTaskDownloading(url)) { startDownloadService(downloadEntity, DownloadStatus.add); } put(url, System.currentTimeMillis()); putStatus(url, DownloadStatus.downloading); } Utils.log(DownloadManager.class.getSimpleName(), "add"); } /** * 继续下载任务 * * @param downloadEntity * @param automatic 是否是自动下载 */ public void resume(DownloadEntity downloadEntity, boolean automatic) { if (downloadEntity != null) { String url = downloadEntity.getUrl(); checkDownloadEntryRecordValidate(url); if (isFileCompleted(url)) { downloadEntity.setStatus(DownloadStatus.done); DataChanger.INSTANCE.notifyDataChanged(downloadEntity); } else if (!isTaskDownloading(url)) { DownloadEntity daoEntity = mDownloadDao.get(downloadEntity.getUrl()); if (automatic) { if (daoEntity != null) { daoEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_RESUME_WAY, DownloadDataHelper.DOWNLOAD_RESUME_AUTO); mDownloadDao.newOrUpdate(daoEntity); } downloadEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_RESUME_WAY, DownloadDataHelper.DOWNLOAD_RESUME_AUTO); } else { if (daoEntity != null) { daoEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_RESUME_WAY, DownloadDataHelper.DOWNLOAD_RESUME_MANUAL); mDownloadDao.newOrUpdate(daoEntity); } downloadEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_RESUME_WAY, DownloadDataHelper.DOWNLOAD_RESUME_MANUAL); } startDownloadService(downloadEntity, DownloadStatus.resume); } put(url, System.currentTimeMillis()); putStatus(url, DownloadStatus.downloading); } Utils.log(DownloadManager.class.getSimpleName(), "resume"); } /** * 添加一个下载任务(WiFi时自动下载) * * @param downloadEntity */ public void subscribe(DownloadEntity downloadEntity) { updateMetaMap(); if (downloadEntity != null) { String url = downloadEntity.getUrl(); checkDownloadEntryRecordValidate(url); if (isFileCompleted(url)) { downloadEntity.setStatus(DownloadStatus.done); DataChanger.INSTANCE.notifyDataChanged(downloadEntity); } else if (!isTaskDownloading(url)) { startDownloadService(downloadEntity, DownloadStatus.subscribe); } put(url, System.currentTimeMillis()); putStatus(url, DownloadStatus.subscribe); } Utils.log(DownloadManager.class.getSimpleName(), "subscribe"); } /** * 根据url到本地sqlite数据库中查找并获取该文件的保存路径, 并检查改路径下文件是否存在,不存在则删除该条无效记录 * * @param url 下载任务的标识url */ private boolean checkDownloadEntryRecordValidate(String url) { DownloadEntity entry = getDownloadEntityByUrl(url); if (entry != null && ((int) entry.getPercent()) != 0) { File file = new File(entry.getPath()); if (!file.exists()) { mDownloadDao.delete(url); Utils.log(DownloadManager.class.getSimpleName(), "文件不存在,删除该条无效数据库记录!"); return true; } } return false; } /** * 检查任务是否已经下载完成 * * @param url * @return */ public boolean isFileCompleted(String url) { DownloadEntity entry = getDownloadEntityByUrl(url); if (entry != null && entry.getPercent() == 100) { Utils.log(DownloadManager.class.getSimpleName(), "文件已经下载完成!"); return true; } return false; } public boolean isTaskDownloading(String url) { if (DataChanger.INSTANCE.getDownloadingTasks().get(url) != null) { Utils.log(DownloadManager.class.getSimpleName(), url + "正在下载!"); return true; } return false; } private Intent getIntent(DownloadEntity entry, DownloadStatus status) { Intent service = new Intent(mContext, DownloadService.class); service.putExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY, entry); service.putExtra(DownloadConfig.KEY_DOWNLOAD_ACTION, status.name()); return service; } /** * 根据url获取某一个下载任务 * * @param url 下载链接 * @return null表示下载列表中不存在该任务,否则返回下载任务 */ // TODO 避免每次访问都从数据库里取数据 @Nullable public DownloadEntity getDownloadEntityByUrl(String url) { if (TextUtils.isEmpty(url)) return null; return mDownloadDao.get(url); } /** * 根据包名获取某一个下载任务 * * @param packageName 包名 * @return null表示下载列表中不存在该任务,否则返回下载任务 */ @Nullable public DownloadEntity getDownloadEntityByPackageName(String packageName) { if (mDownloadDao == null) return null; List downloadList = mDownloadDao.getAll(); if (downloadList == null) return null; for (DownloadEntity downloadEntity : downloadList) { if (packageName.equals(downloadEntity.getPackageName())) { return downloadEntity; } } return null; } public void put(String url, long time) { lastTimeMap.put(url, time); } public void putQueue(String name, LinkedBlockingQueue queue) { platformMap.put(name, queue); } public LinkedBlockingQueue getQueue(String name) { return platformMap.get(name); } public void removePlatform(String name, String platform) { LinkedBlockingQueue queue = platformMap.get(name); if (queue != null) { queue.remove(platform); platformMap.put(name, queue); } } public void initGameMap() { gameMap.clear(); List list = getAllDownloadEntity(); if (list != null && list.size() != 0) { String name; for (DownloadEntity downloadEntity : list) { name = downloadEntity.getName(); ArrayMap map = gameMap.get(name); if (map == null) { map = new ArrayMap<>(); gameMap.put(name, map); } map.put(downloadEntity.getPlatform(), downloadEntity); } } } /** * 获取所有下载列表中的任务 */ @NonNull public List getAllDownloadEntity() { if (CommonDebug.IS_DEBUG) { CommonDebug.logMethodName(this); } List all = mDownloadDao.getAll(); return all != null ? all : new ArrayList<>(); } public List getAllSimulatorDownloadEntity() { List downloadEntityList = getAllDownloadEntity(); ArrayList filteredDownloadEntityList = new ArrayList<>(); for (DownloadEntity downloadEntity : downloadEntityList) { if (ExtensionsKt.isSimulatorGame(downloadEntity) && downloadEntity.getStatus() == DownloadStatus.done) { filteredDownloadEntityList.add(downloadEntity); } } return filteredDownloadEntityList; } /** * 获取所有下载列表中的任务排除静默更新 */ public List getAllDownloadEntityExcludeSilentUpdate() { if (CommonDebug.IS_DEBUG) { CommonDebug.logMethodName(this); } List all = getAllDownloadEntity(); return filterSilentDownloadTask(all); } private ArrayList filterSilentDownloadTask(List downloadEntityList) { ArrayList filteredDownloadEntityList = new ArrayList<>(); if (downloadEntityList == null) return filteredDownloadEntityList; for (DownloadEntity downloadEntity : downloadEntityList) { if (!ExtensionsKt.isSimulatorGame(downloadEntity)) { if (!Constants.SILENT_UPDATE.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE)) && !Constants.SIMULATOR_DOWNLOAD.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE))) { filteredDownloadEntityList.add(downloadEntity); } } else { if (downloadEntity.getStatus() != DownloadStatus.done) { filteredDownloadEntityList.add(downloadEntity); } } } return filteredDownloadEntityList; } public ArrayMap getEntryMap(String name) { return gameMap.get(name); } private void putStatus(String url, DownloadStatus status) { statusMap.put(url, status); onTaskStatusChanged(getDownloadEntityByUrl(url)); } public DownloadStatus getStatus(String url) { return statusMap.get(url); } public ArrayMap getStatusMap() { return statusMap; } public void sendMessageDelayed(Message msg, long delayMillis) { mHandler.sendMessageDelayed(msg, delayMillis); } /** * 根据url取消下载,并删除已下载的文件 * * @param url */ public void cancel(String url) { cancel(url, true, false); } /** * 根据url取消下载,并删除已下载的文件 * * @param url * @param automatic 是否是安装完自动删除 */ public void cancel(String url, boolean isDeleteFile, boolean automatic) { DownloadEntity entry = mDownloadDao.get(url); if (entry != null) { if (isDeleteFile) { FileUtils.deleteFile(entry.getPath()); } mDownloadDao.delete(url); Utils.log(DownloadManager.class.getSimpleName(), "cancel==>record were deleted!"); } if (entry != null) { if (automatic) { entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_AUTO); } else { entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_MANUAL); } entry.setStatus(DownloadStatus.cancel); // startDownloadService(entry, DownloadStatus.cancel); // 将原来安装完成后在 downloadService 完成的功能放到这里,尝试避免因为 ANR 造成闪退 AppExecutor.getUiExecutor().executeWithDelay(() -> { mDownloadDao.removeErrorMessage(entry.getUrl()); DownloadTask task = DataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl()); if (task != null) { task.cancel(); // 改任务队列的状态 DataChanger.INSTANCE.getDownloadingTasks().remove(entry.getUrl()); DataChanger.INSTANCE.notifyDataChanged(entry); } DataChanger.INSTANCE.getDownloadEntries().remove(entry.getUrl()); DataChanger.INSTANCE.notifyDataChanged(entry); DownloadStatusManager.getInstance().onTaskCancelled(entry); Utils.log(DownloadManager.class.getSimpleName(), "cancel"); }, 0); } } /** * 取消并删除所有下载任务(包括下载中、等待、暂停状态的任务) */ public void cancelAll() { for (DownloadEntity entry : DataChanger.INSTANCE.getDownloadEntries().values()) { cancel(entry.getUrl(), true, false); } Utils.log(DownloadManager.class.getSimpleName(), "cancel all"); } /** * 开始所有下载任务 */ public void startAll() { for (DownloadEntity entry : downloadingMap.values()) { add(entry); } Utils.log(DownloadManager.class.getSimpleName(), "start all"); } /** * 暂停所有正在下载的任务 */ public void pauseAll() { for (DownloadEntity entity : DataChanger.INSTANCE.getDownloadEntries().values()) { pause(entity.getUrl()); } Utils.log(DownloadManager.class.getSimpleName(), "pause all"); } /** * 根据任务url暂停下载 * * @param url */ public void pause(String url) { checkDownloadEntryRecordValidate(url); DownloadEntity entry = DataChanger.INSTANCE.getDownloadEntries().get(url); if (entry != null) { startDownloadService(entry, DownloadStatus.pause); put(url, System.currentTimeMillis()); statusMap.put(url, DownloadStatus.pause); } Utils.log(DownloadManager.class.getSimpleName(), "pause"); } public void pause(DownloadEntity entity) { if (entity != null) { pause(entity.getUrl()); } } /** * 初始化下载 * 1.修正下载状态(由于进程突然中断导致的状态异常) * 2.启动下载服务 * 3.检查是否显示下载通知栏 */ public void initDownloadService() { final List urlList = new ArrayList<>(DataChanger.INSTANCE.getDownloadingTasks().keySet()); for (DownloadEntity downloadEntity : getAllDownloadEntity()) { if (!urlList.contains(downloadEntity.getUrl()) && (downloadEntity.getStatus().equals(DownloadStatus.downloading) || downloadEntity.getStatus().equals(DownloadStatus.waiting))) { downloadEntity.setStatus(DownloadStatus.subscribe); mDownloadDao.newOrUpdate(downloadEntity); DataChanger.INSTANCE.notifyDataChanged(downloadEntity); } } startDownloadService(); checkRetryDownload(); } public void addObserver(DataWatcher dataWatcher) { Utils.log(DownloadManager.class.getSimpleName(), "addObserver"); DataChanger.INSTANCE.addObserver(dataWatcher); } public void removeObserver(DataWatcher dataWatcher) { Utils.log(DownloadManager.class.getSimpleName(), "removeObserver"); DataChanger.INSTANCE.deleteObserver(dataWatcher); } public void startDownloadService() { Intent serviceIntent = new Intent(mContext, DownloadService.class); // 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !PackageUtils.isAppOnForeground(mContext)) { serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND); mContext.startForegroundService(serviceIntent); } else { /* * sentry上报oppo手机无法启动服务,原因:OPPO手机自动熄屏一段时间后,会启用系统自带的电量优化管理,禁止一切自启动的APP(用户设置的自启动白名单除外),所以在服务启动的地方进行try/catch防止崩溃, * https://sentry.ghzs.com/organizations/lightgame/issues/10707/?project=22&query=is%3Aunresolved+assigned%3Ame&statsPeriod=14d, * https://stackoverflow.com/questions/38764497/security-exception-unable-to-start-service-user-0-is-restricted */ ExtensionsKt.tryWithDefaultCatch(() -> { mContext.startService(serviceIntent); return null; }); } } private void startDownloadService(DownloadEntity downloadEntity, DownloadStatus status) { // 在启动服务时添加该条下载的网络状态 if (status == DownloadStatus.add || status == DownloadStatus.resume) { String network = DeviceUtils.getNetwork(mContext); DownloadEntity daoEntity = mDownloadDao.get(downloadEntity.getUrl()); if (daoEntity != null) { daoEntity.getMeta().put(DownloadEntity.NETWORK_STATUS_KEY, network); mDownloadDao.newOrUpdate(daoEntity); } downloadEntity.getMeta().put(DownloadEntity.NETWORK_STATUS_KEY, network); } if (status == DownloadStatus.add || status == DownloadStatus.subscribe) { if (downloadEntity.getMeta().get(DownloadDataHelper.DOWNLOAD_FIRST_START) == null) { downloadEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_FIRST_START, "YES"); } } Intent serviceIntent = getIntent(downloadEntity, status); // 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !PackageUtils.isAppOnForeground(mContext)) { serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND); mContext.startForegroundService(serviceIntent); } else { mContext.startService(serviceIntent); } } public void checkRetryDownload() { if (!NetworkUtils.isWifiConnected(mContext)) return; for (DownloadEntity downloadEntity : DownloadManager.getInstance(mContext).getAllDownloadEntityExcludeSilentUpdate()) { if (DownloadStatus.neterror.equals(downloadEntity.getStatus()) || DownloadStatus.timeout.equals(downloadEntity.getStatus()) || DownloadStatus.subscribe.equals(downloadEntity.getStatus())) { DownloadManager.getInstance(mContext).put(downloadEntity.getUrl(), System.currentTimeMillis()); Message msg = Message.obtain(); msg.what = DownloadConfig.CONTINUE_DOWNLOAD_AUTO_TASK; msg.obj = downloadEntity.getUrl(); DownloadManager.getInstance(mContext).sendMessageDelayed(msg, 1000); } Utils.log("checkRetryDownload::" + downloadEntity.getStatus()); } } /** * @param updateList 更新/插件化数据 * @return null: 不显示小红点 */ @Nullable public String getDownloadOrUpdateCount(List updateList) { boolean containsReadDownloadingTask = false; // 存在已读的下载中任务 boolean showRedPoint = false; int downloadingSize = 0; for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentUpdate()) { if (DownloadStatus.done.equals(downloadEntity.getStatus())) { String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK); if (TextUtils.isEmpty(mark)) showRedPoint = true; } else { // 存在已读的下载中任务就直接不返回下载中数量,因为在新建下载时就执行了将所有下载中任务的已读变为未读的操作 if (!TextUtils.isEmpty(downloadEntity.getMeta().get(DOWNLOADING_IS_READ_MARK))) { containsReadDownloadingTask = true; } downloadingSize++; } } if (downloadingSize != 0 && !containsReadDownloadingTask) { return String.valueOf(downloadingSize); } if (showRedPoint) return ""; if (updateList != null) { // 首页永久忽略的插件化列表 List permanentInactiveUpdateList = HomePluggableHelper.getPermanentInactivePluggablePackage(); for (GameUpdateEntity updateEntity : updateList) { if (updateEntity.isShowPlugin(PluginLocation.only_index) && !mUpdateMarks.contains(updateEntity.getId() + updateEntity.getPackageName())) { // 判断该更新的的包名是否被永久忽略 if (permanentInactiveUpdateList != null) { boolean isPluggablePermanentInactive = false; for (HomePluggableFilterEntity filterEntity : permanentInactiveUpdateList) { if (filterEntity.getPkgName().equals(updateEntity.getPackageName())) { isPluggablePermanentInactive = true; } } if (!isPluggablePermanentInactive) { return ""; } } else { return ""; } } } } return null; } public int getUnreadUpdateCount(List updateList) { int unreadUpdateCount = 0; if (updateList != null) { for (GameUpdateEntity updateEntity : updateList) { if (updateEntity.isShowPlugin(PluginLocation.only_index) && !mUpdateMarks.contains(updateEntity.getId() + updateEntity.getPackageName())) { unreadUpdateCount++; } } } return unreadUpdateCount; } public boolean isContainsUnreadDownloadedTask() { for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentUpdate()) { if (DownloadStatus.done.equals(downloadEntity.getStatus())) { String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK); if (TextUtils.isEmpty(mark)) { return true; } } } return false; } /** * 标记下载已完成的任务为已读 (用于下载管理页入口的 toolbar 红点显示) */ public void markDownloadedTaskAsRead() { AppExecutor.getIoExecutor().execute(() -> { boolean markHasChanged = false; List all = getAllDownloadEntityExcludeSilentUpdate(); for (DownloadEntity downloadEntity : all) { DownloadStatus status = downloadEntity.getStatus(); if (status == DownloadStatus.done) { String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK); if (TextUtils.isEmpty(mark)) { downloadEntity.getMeta().put(DOWNLOADED_IS_READ_MARK, DOWNLOADED_IS_READ_MARK); mDownloadDao.newOrUpdate(downloadEntity); if (!markHasChanged) markHasChanged = true; } } } if (markHasChanged) { EventBus.getDefault().post(new EBDownloadStatus("download", "", "", "", "", "")); } }); } /** * 标记下载中任务为已读状态(用于下载页及外部toolbar) */ public void markDownloadingTaskAsRead() { markDownloadingTaskAsReadOrUnread(true); } /** * 标记下载中任务为未读状态(用于下载页及外部toolbar) */ public void markDownloadingTaskAsUnread() { markDownloadingTaskAsReadOrUnread(false); } /** * 更改下载中任务的已读状态(用于下载页及外部toolbar) */ private void markDownloadingTaskAsReadOrUnread(boolean isRead) { AppExecutor.getIoExecutor().execute(() -> { boolean markHasChanged = false; List all = getAllDownloadEntityExcludeSilentUpdate(); for (DownloadEntity downloadEntity : all) { if (downloadEntity.getStatus() != DownloadStatus.done) { if (isRead) { String mark = downloadEntity.getMeta().get(DOWNLOADING_IS_READ_MARK); if (TextUtils.isEmpty(mark)) { downloadEntity.getMeta().put(DOWNLOADING_IS_READ_MARK, DOWNLOADING_IS_READ_MARK); mDownloadDao.newOrUpdate(downloadEntity); if (!markHasChanged) markHasChanged = true; } } else { downloadEntity.getMeta().put(DOWNLOADING_IS_READ_MARK, ""); mDownloadDao.newOrUpdate(downloadEntity); if (!markHasChanged) markHasChanged = true; } } } if (markHasChanged) { EventBus.getDefault().post(new EBDownloadStatus("download", "", "", "", "", "")); } }); } /** * 标记可用更新为已读 (用于下载管理页入口的 toolbar 红点显示) */ public void markUpdatableTaskAsRead() { AppExecutor.getIoExecutor().execute(() -> { boolean markHasChanged = false; ArrayList updates = PackageRepository.INSTANCE.getGameUpdate(); for (GameUpdateEntity update : updates) { String mark = update.getId() + update.getPackageName(); if (!mUpdateMarks.contains(mark)) { mUpdateMarks.add(mark); if (!markHasChanged) markHasChanged = true; } } if (markHasChanged) { saveUpdateMarkToStorage(); EventBus.getDefault().post(new EBDownloadStatus("download", "", "", "", "", "")); } }); } /** * 将可用更新标记为已读的事件 */ public void saveUpdateMarkToStorage() { ArrayList updates = PackageRepository.INSTANCE.getGameUpdate(); if (updates.size() == mUpdateMarks.size()) { SPUtils.setStringSet(UPDATE_IS_READ_MARK, mUpdateMarks); return; } Set marks = new HashSet<>(); for (GameUpdateEntity update : updates) { String mark = update.getId() + update.getPackageName(); marks.add(mark); } SPUtils.setStringSet(UPDATE_IS_READ_MARK, marks); } public void updateDownloadEntity(DownloadEntity downloadEntity) { mDownloadDao.newOrUpdate(downloadEntity); } public void startDownload(String url) { put(url, System.currentTimeMillis()); Message msg = Message.obtain(); msg.what = DownloadConfig.CONTINUE_DOWNLOAD_AUTO_TASK; msg.obj = url; sendMessageDelayed(msg, 1000); } public static void updateMetaMap() { HashMap map = new HashMap<>(); map.put(HttpDnsManager.APP_VERSION, BuildConfig.VERSION_NAME); map.put(HttpDnsManager.CHANNEL, HaloApp.getInstance().getChannel()); map.put(HttpDnsManager.GID, HaloApp.getInstance().getGid()); map.put(HttpDnsManager.OAID, HaloApp.getInstance().getOAID()); map.put(HttpDnsManager.USER_ID, UserManager.getInstance().getUserId()); map.put(HttpDnsManager.IMEI, MetaUtil.getBase64EncodedIMEI()); HttpDnsManager.metaMap = map; } }