328 lines
13 KiB
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();
|
|
}
|
|
|
|
}
|