package com.gh.download; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; import android.text.TextUtils; import com.gh.common.exposure.ExposureEvent; import com.gh.common.util.AppDebugConfig; import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DialogUtils; import com.gh.common.util.GdtHelper; import com.gh.common.util.MD5Utils; import com.gh.common.util.PackageUtils; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.eventbus.EBDownloadStatus; import com.gh.gamecenter.manager.PackagesManager; 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.FileUtils; import com.lightgame.utils.Utils; import com.qq.gdt.action.ActionType; import org.greenrobot.eventbus.EventBus; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import static android.os.Build.MANUFACTURER; public class DownloadManager implements DownloadStatusListener { private static DownloadManager mInstance; private static Gson gson = new Gson(); private Context mContext; private Handler mHandler; private ArrayMap lastTimeMap; private ArrayMap> platformMap; private ArrayMap> gameMap; private ArrayMap statusMap; private ArrayMap downloadingMap; private DownloadDao mDownloadDao; @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); DownloadNotification.showDownloadingNotification(mContext); DownloadManager.getInstance(mContext).putStatus(entity.getUrl(), DownloadStatus.delete); downloadingMap.remove(entity.getUrl()); } @Override public void onTaskAdded(DownloadEntity entity) { EventBus.getDefault().post(new EBDownloadStatus("download")); DownloadNotification.showDownloadingNotification(mContext); downloadingMap.put(entity.getUrl(), entity); } @Override public void onTaskError(DownloadEntity entity) { DownloadNotification.showDownloadingNotification(mContext); downloadingMap.remove(entity.getUrl()); } @Override public void onTaskStatusChanged(DownloadEntity entity) { } @Override public void onTaskDone(DownloadEntity entity) { DownloadNotification.showDownloadingNotification(mContext); DownloadNotification.showDownloadDoneNotification(mContext, entity); downloadingMap.remove(entity.getUrl()); } @Override public void onTaskPaused(DownloadEntity entity) { } private DownloadManager(Context context) { mContext = context.getApplicationContext(); mDownloadDao = DownloadDao.getInstance(mContext); //TODO unregister this DownloadStatusManager.getInstance().registerTaskStatusListener(this); // 只有下载模块需要这坨东西,因此移动到这里初始化 ConnectionUtils.initHttpsUrlConnection(context); // 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: if (lastTimeMap.get(url) != null && System.currentTimeMillis() - lastTimeMap.get(url) >= 1000) { DownloadEntity downloadEntity = getDownloadEntityByUrl(url); if (downloadEntity != null) add(downloadEntity); } 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 = getAll(); 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) { // 安装指引 if ("Huawei".equalsIgnoreCase(MANUFACTURER) || "Oppo".equalsIgnoreCase(MANUFACTURER)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences.Editor edit = sp.edit(); if (sp.getBoolean("InstallHint" + PackageUtils.getVersionName(), true)) { try { DialogUtils.showInstallHintDialog(context, () -> edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply()); } catch (Exception exception) { exception.printStackTrace(); edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply(); } } } DownloadEntity downloadEntity = new DownloadEntity(); downloadEntity.setUrl(apkEntity.getUrl()); downloadEntity.setName(gameEntity.getName()); downloadEntity.setPath(FileUtils.getDownloadPath(context, MD5Utils.getContentMD5(gameEntity.getName() + "_" + System.currentTimeMillis()) + ".apk")); downloadEntity.setETag(apkEntity.getEtag()); downloadEntity.setIcon(gameEntity.getIcon()); downloadEntity.setPlatform(apkEntity.getPlatform()); downloadEntity.setPackageName(apkEntity.getPackageName()); downloadEntity.setGameId(gameEntity.getId()); downloadEntity.setEntrance(entrance); downloadEntity.setExposureTrace(gson.toJson(traceEvent)); downloadEntity.setLocation(location); downloadEntity.setVersionName(apkEntity.getVersion()); int installed = 0; for (ApkEntity apk : gameEntity.getApk()) { if (PackagesManager.INSTANCE.isInstalled(apk.getPackageName())) { installed++; } } downloadEntity.setInstalled(installed); if ("更新".equals(method)) { downloadEntity.setUpdate(true); } else if ("插件化".equals(method)) { downloadEntity.setPluggable(true); } downloadEntity.setPlugin(gameEntity.getTag() != null && gameEntity.getTag().size() != 0); if (isSubscribe) { DownloadManager.getInstance(context).subscribe(downloadEntity); } else { 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); // 收集下载数据 DataCollectionUtils.uploadDownload(context, downloadEntity, "开始"); GdtHelper.INSTANCE.logAction(ActionType.DOWNLOAD_APP, GdtHelper.GAME_ID, downloadEntity.getGameId(), GdtHelper.PLATFORM, downloadEntity.getPlatform()); } /** * 添加一个下载任务 * * @param downloadEntity */ public void add(DownloadEntity downloadEntity) { 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"); } /** * 添加一个下载任务(WiFi时自动下载) * * @param downloadEntity */ public void subscribe(DownloadEntity downloadEntity) { 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表示下载列表中不存在该任务,否则返回下载任务 */ public DownloadEntity getDownloadEntityByUrl(String url) { if (TextUtils.isEmpty(url)) return null; return mDownloadDao.get(url); } /** * 根据包名获取某一个下载任务 * * @param packageName 包名 * @return null表示下载列表中不存在该任务,否则返回下载任务 */ public DownloadEntity getDownloadEntityByPackageName(String packageName) { for (DownloadEntity downloadEntity : mDownloadDao.getAll()) { 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 = getAll(); 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); } } } /** * 获取所有下载列表中的任务 * * @return null表示没有下载任务 */ public List getAll() { if (CommonDebug.IS_DEBUG) { CommonDebug.logMethodName(this); } return mDownloadDao.getAll(); } 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); } public void cancel(String url, boolean isDeleteFile) { DownloadEntity entry = mDownloadDao.get(url); if (entry != null) { if (isDeleteFile) { FileUtils.deleteFile(entry.getPath()); } mDownloadDao.delete(url); Utils.log(DownloadManager.class.getSimpleName(), "cancel==>file and record were deleted!"); } if (entry != null) { entry.setStatus(DownloadStatus.cancel); startDownloadService(entry, DownloadStatus.cancel); Utils.log(DownloadManager.class.getSimpleName(), "cancel"); } } /** * 取消并删除所有下载任务(包括下载中、等待、暂停状态的任务) */ public void cancelAll() { for (DownloadEntity entry : DataChanger.INSTANCE.getDownloadEntries().values()) { cancel(entry.getUrl(), true); } 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()); } } /** * 检查数据库中当前是下载状态,但并未在下载进程中的下载数据 */ public void checkAll() { final List urlList = new ArrayList<>(DataChanger.INSTANCE.getDownloadingTasks().keySet()); for (DownloadEntity downloadEntity : getAll()) { if (!urlList.contains(downloadEntity.getUrl()) && downloadEntity.getStatus().equals(DownloadStatus.downloading)) { downloadEntity.setStatus(DownloadStatus.pause); mDownloadDao.newOrUpdate(downloadEntity); DataChanger.INSTANCE.notifyDataChanged(downloadEntity); } } // 开启下载服务 startDownloadServiceInBackground(); DownloadNotification.showDownloadingNotification(mContext); } 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 startDownloadServiceInBackground() { mContext.startService(new Intent(mContext, DownloadService.class)); } public void startDownloadService(DownloadEntity downloadEntity, DownloadStatus status) { Intent serviceIntent = getIntent(downloadEntity, status); // 当满足系统版本大于 8.0 、应用在后台运行时以前台服务开启 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !HaloApp.getInstance().isRunningForeground) { serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND); mContext.startForegroundService(serviceIntent); } else { mContext.startService(serviceIntent); } } // // public void removeObservers() { // Utils.log(DownloadManager.class.getSimpleName(), "removeObserver"); // DataChanger.INSTANCE.deleteObservers(); // } }