package com.gh.ndownload; import android.app.Activity; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; import android.text.TextUtils; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import com.gh.download.DownloadManager; import com.gh.gamecenter.SplashScreenActivity; import com.gh.gamecenter.common.base.GlobalActivityManager; import com.gh.ndownload.suspendwindow.NDownloadDrawOverlayPermissionWindowController; import com.gh.ndownload.suspendwindow.NDownloadSuspendWindowController; import com.gh.ndownload.suspendwindow.utils.NDownloadSuspendWindowHelper; import com.lightgame.BuildConfig; import com.lightgame.download.DownloadConfig; import com.lightgame.download.DownloadDao; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadStatus; import com.lightgame.download.ForegroundNotificationManager; import com.lightgame.utils.Utils; import java.util.ArrayList; import java.util.Random; public class NDownloadService extends Service { public static final String KEY_SERVICE_ACTION = "service_action"; public static final String START_FOREGROUND = "start_foreground"; public static final String ACTION_SHOW_SUSPEND_WINDOW = "action_show_suspend_window"; private static final String SERVICE_CHANNEL_ID = "Halo_Download"; private static NDownloadService sService = null; private ForegroundNotificationManager mForegroundNotificationManager; private NDownloadSuspendWindowController mDownloadSuspendWindowController; private NDownloadDrawOverlayPermissionWindowController mDrawOverlayPermissionWindowController; @Nullable public static NDownloadService getService() { return sService; } @Override public void onCreate() { super.onCreate(); sService = this; mForegroundNotificationManager = new ForegroundNotificationManager(this, this.getApplication()); Utils.log(NDownloadService.class.getSimpleName(), "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Utils.log(NDownloadService.class.getSimpleName(), "onStartCommand"); int randomNotificationId = getNextIntExcludeZero(); if (intent != null && intent.getExtras() != null) { String serviceAction = intent.getStringExtra(KEY_SERVICE_ACTION); if (START_FOREGROUND.equals(serviceAction)) { startForegroundIfNeeded(randomNotificationId); } else if (ACTION_SHOW_SUSPEND_WINDOW.equals(serviceAction)) { DownloadEntity entry = (DownloadEntity) intent.getSerializableExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY); showDownloadSuspendWindowIfNeeded(entry); return START_NOT_STICKY; } String statusString = intent.getStringExtra(DownloadConfig.KEY_DOWNLOAD_ACTION); if (!TextUtils.isEmpty(statusString)) { // 看不懂的操作 DownloadStatus status = DownloadStatus.valueOf(statusString); DownloadEntity entry = (DownloadEntity) intent.getSerializableExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY); DownloadEntity downloadEntity = DownloadDao.getInstance(this).get(entry.getUrl()); if (downloadEntity == null) { // 设置开始下载时间(排序用到) entry.setStart(System.currentTimeMillis()); } // 置换最新状态 entry.setStatus(status); // 删除下载错误信息 entry.setError(""); DownloadDao.getInstance(this).removeErrorMessage(entry.getUrl()); if (BuildConfig.DEBUG) { Utils.log(NDownloadService.class.getSimpleName(), entry.getPackageName() + "==>" + entry.getStatus().getStatus()); } switch (status) { case subscribe: subscribeDownload(entry); break; case add: addDownload(entry); break; case pause: case timeout: case neterror: case diskioerror: case diskisfull: pauseDownload(entry); break; case resume: resumeDownload(entry); break; case cancel: cancelDownload(entry); break; default: if (BuildConfig.DEBUG) { Utils.log(NDownloadService.class.getSimpleName(), "无法处理的下载状态:" + entry.getStatus().getStatus()); } break; } } else { if (BuildConfig.DEBUG) { Utils.log(NDownloadService.class.getSimpleName(), "启动了一个空的下载服务"); } } } else { // 无参时也默认以前台启动 startForegroundIfNeeded(randomNotificationId); } stopForegroundIfNeeded(randomNotificationId); // 不需要 intent 为空的重建 return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); sService = null; if (mDownloadSuspendWindowController != null) { mDownloadSuspendWindowController.dismiss(); } if (mDrawOverlayPermissionWindowController != null) { mDrawOverlayPermissionWindowController.dismiss(); } } @Override public IBinder onBind(Intent intent) { return null; } synchronized void subscribeDownload(DownloadEntity entry) { DownloadDao.getInstance(this).newOrUpdate(entry); NDataChanger.INSTANCE.notifyDataChanged(entry); DownloadManager.getInstance().onTaskAdded(entry); } synchronized void addDownload(DownloadEntity entry) { if (NDataChanger.INSTANCE.getDownloadingTaskUrlSet().size() >= DownloadConfig.MAX_DOWNLOADING_SIZE) { // 1.改任务队列的状态 entry.setStatus(DownloadStatus.waiting); // 2.改数据库状态 DownloadDao.getInstance(this).newOrUpdate(entry); // 3.通知更新 NDataChanger.INSTANCE.notifyDataChanged(entry); } else { startDownload(entry); } DownloadManager.getInstance().onTaskAdded(entry); } synchronized void pauseDownload(DownloadEntity entry) { if (entry != null) { entry.setStatus(DownloadStatus.pause); // 1.改任务队列的状态 NDownloadBridge.INSTANCE.pause(entry.getUrl()); NDataChanger.INSTANCE.pauseDownloadingTask(entry.getUrl()); // 2.改数据库状态 DownloadDao.getInstance(this).update(entry, false); } } synchronized void resumeDownload(DownloadEntity entry) { addDownload(entry); } synchronized void cancelDownload(DownloadEntity downloadEntity) { if (NDataChanger.INSTANCE.getDownloadingTaskUrlSet().contains(downloadEntity.getUrl())) { NDownloadBridge.INSTANCE.cancel(downloadEntity.getUrl()); // 改任务队列的状态 NDataChanger.INSTANCE.getDownloadingTaskUrlSet().remove(downloadEntity.getUrl()); NDataChanger.INSTANCE.notifyDataChanged(downloadEntity); } NDataChanger.INSTANCE.notifyDataChanged(downloadEntity); DownloadManager.getInstance().onTaskCancelled(downloadEntity); } synchronized void startDownload(DownloadEntity entry) { if (!NDataChanger.INSTANCE.getDownloadingTaskUrlSet().contains(entry.getUrl())) { // 显示下载悬浮窗 showDownloadSuspendWindowIfNeeded(entry); // 刷新初始状态 NDataChanger.INSTANCE.notifyDataChanged(entry); entry.getMeta().put(DownloadEntity.DOWNLOAD_STARTUP_STATUS_KEY, entry.getStatus().getStatus()); // 1.改任务队列的状态 entry.setStatus(DownloadStatus.downloading); NDataChanger.INSTANCE.getDownloadingTaskUrlSet().add(entry.getUrl()); // 2.改数据库状态 DownloadDao.getInstance(this).newOrUpdate(entry); // 3.执行下载 NDownloadBridge.INSTANCE.download(entry); // 4.通知更新 NDataChanger.INSTANCE.notifyDataChanged(entry); } else { Utils.log("Fuck this, we received multiple download request for the same url"); } } /** * 找到下一个处于 waiting 状态的任务继续 * @param url 需要移除的 url 对应的数据 */ public synchronized void removeFromDataChangerAndResumeWaitingTask(String url) { NDataChanger.INSTANCE.getDownloadingTaskUrlSet().remove(url); ArrayList downloadEntitySnapshotList = DownloadManager.getInstance().getAllDownloadEntitySnapshots(); for (DownloadEntity entry : downloadEntitySnapshotList) { if (DownloadStatus.waiting.equals(entry.getStatus())) { // 刷新状态(从等待中到开始下载,状态应该变为继续下载) entry.setStatus(DownloadStatus.resume); startDownload(entry); if (BuildConfig.DEBUG) { Utils.log(NDownloadService.class.getSimpleName(), entry.getPackageName() + "==>" + entry.getStatus().getStatus()); } return; } } } private void startForegroundIfNeeded(int notificationId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(SERVICE_CHANNEL_ID, "Halo Download", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); manager.createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, SERVICE_CHANNEL_ID) .setSmallIcon(com.lightgame.R.drawable.ic_download_notification) .build(); if (mForegroundNotificationManager != null) { mForegroundNotificationManager.notify(notificationId, notification); } } } private void stopForegroundIfNeeded(int notificationId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (mForegroundNotificationManager != null) { mForegroundNotificationManager.cancel(notificationId); } } } private void showDownloadSuspendWindowIfNeeded(DownloadEntity entry) { if (!NDownloadSuspendWindowHelper.shouldSkipDownloadEntity(getApplicationContext(), entry)) { if (NDownloadSuspendWindowHelper.canDrawOverLayer(getApplicationContext())) {// 已开启悬浮窗权限,直接显示悬浮窗 showDownloadSuspendWindow(); } else {// 未开启悬浮窗权限,显示开启悬浮窗权限引导窗口 Activity topActivity = GlobalActivityManager.INSTANCE.getCurrentActivity(); if (!NDownloadSuspendWindowHelper.isDrawOverlayPermissionWindowShown() && topActivity != null && !(topActivity instanceof SplashScreenActivity)) { showDownloadDrawOverlayPermissionWindow(topActivity, entry); } } } } /** * 获取 0 以外的随机 int 数值 */ private int getNextIntExcludeZero() { int randomNumber = new Random().nextInt(); if (randomNumber != 0) { return randomNumber; } else { return getNextIntExcludeZero(); } } private void showDownloadSuspendWindow() { if (mDownloadSuspendWindowController == null) { mDownloadSuspendWindowController = new NDownloadSuspendWindowController(getApplication()); } mDownloadSuspendWindowController.show(); } private void showDownloadDrawOverlayPermissionWindow(Activity activity, DownloadEntity downloadEntity) { if (mDrawOverlayPermissionWindowController == null) { mDrawOverlayPermissionWindowController = new NDownloadDrawOverlayPermissionWindowController(getApplication()); mDrawOverlayPermissionWindowController.setOnGrantDrawOverlayPermissionListener(this::showDownloadSuspendWindow); } mDrawOverlayPermissionWindowController.setCurrentDownloadEntity(downloadEntity); mDrawOverlayPermissionWindowController.show(activity); NDownloadSuspendWindowHelper.markDrawOverlayPermissionWindowShown(); } }