Files
assistant-android/app/src/main/java/com/gh/ndownload/NDownloadService.java

328 lines
13 KiB
Java

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<DownloadEntity> 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();
}
}