Compare commits

...

6 Commits

Author SHA1 Message Date
732e54368e feat: 内测包启用 compose 2023-02-21 14:17:54 +08:00
1eb8c912ad Merge remote-tracking branch 'origin/pack-v5.19.1-731-multithread_download' into pack-v5.19.0-730-beta
# Conflicts:
#	app/src/main/java/com/gh/download/DownloadDataHelper.kt
#	app/src/main/java/com/gh/ndownload/NDownloadBridge.kt
#	libraries/LGLibrary
2023-02-21 14:03:21 +08:00
5ddd86f184 fix: 修复会触发两次下载完成事件的问题
fix: 修复会触发两次下载完成事件的问题
2023-02-21 11:45:00 +08:00
4b182a8485 feat: 底层下载切换至多线程 2023-02-21 09:47:57 +08:00
f2086f1bda feat: 添加简单的内测版配置 2023-02-06 16:17:32 +08:00
7c0e380415 feat: 底层下载切换至多线程 2023-02-06 14:44:41 +08:00
59 changed files with 1075 additions and 114 deletions

View File

@ -165,6 +165,7 @@ android {
// publish release host
publish {
dimension "env"
versionNameSuffix "-beta"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""

View File

@ -119,6 +119,8 @@
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<service android:name = "com.gh.ndownload.NDownloadService" />
<activity
android:name="com.gh.gamecenter.SplashScreenActivity"
android:configChanges="keyboardHidden|orientation|screenSize"

View File

@ -594,6 +594,7 @@ public class BindingAdapters {
case pause:
case timeout:
case neterror:
case diskisfull:
case waiting:
progressBar.setText(R.string.downloading);
if (downloadEntity.isPluggable() && PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {

View File

@ -22,8 +22,12 @@ import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.feature.entity.ApkEntity
import com.gh.gamecenter.feature.entity.SimulatorEntity
import com.gh.gamecenter.entity.TrackableEntity
import com.gh.ndownload.NDataChanger
import com.halo.assistant.HaloApp
import com.lightgame.download.*
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import java.lang.ref.WeakReference
import java.text.DecimalFormat
@ -95,6 +99,9 @@ class SimulatorDownloadManager private constructor() {
downloadDialog?.dismiss()
}
}
DownloadStatus.diskisfull == downloadEntity.status -> {
ToastUtils.showToast("存储空间已满,下载任务已暂停")
}
DownloadStatus.neterror == downloadEntity.status -> {
ToastUtils.showToast("网络不稳定,下载任务已暂停")
}
@ -290,13 +297,16 @@ class SimulatorDownloadManager private constructor() {
HaloApp.put(simulator.name, simulator)
if (entity != null) {
when (entity.status) {
DownloadStatus.pause, DownloadStatus.subscribe,
DownloadStatus.neterror, DownloadStatus.timeout -> {
DownloadStatus.pause,
DownloadStatus.subscribe,
DownloadStatus.neterror,
DownloadStatus.timeout,
DownloadStatus.diskisfull -> {
DownloadManager.getInstance().addObserver(dataWatcher)
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance().resume(entity, true) }, 200)
downloadDialog?.show()
}
DownloadStatus.done -> DataChanger.notifyDataChanged(entity)
DownloadStatus.done -> NDataChanger.notifyDataChanged(entity)
else -> createDownload(apkEntity, simulator)
}

View File

@ -262,6 +262,7 @@ public class DetailDownloadUtils {
case downloading:
case redirected:
case pause:
case diskisfull:
case overflow:
String downloadingText = "游戏加载中 " + downloadEntity.getPercent() + "%";
String resumeText = "继续加载 " + downloadEntity.getPercent() + "%";
@ -348,6 +349,7 @@ public class DetailDownloadUtils {
case timeout:
case neterror:
case subscribe:
case diskisfull:
case pause:
viewHolder.mDownloadPb.setText("继续加载 " + viewHolder.downloadEntity.getPercent() + "%");
viewHolder.mDownloadPb.setButtonStyle(DownloadButton.ButtonStyle.DOWNLOADING_NORMAL);

View File

@ -309,6 +309,7 @@ object DownloadItemUtils {
DownloadStatus.timeout,
DownloadStatus.neterror,
DownloadStatus.subscribe,
DownloadStatus.diskisfull,
DownloadStatus.overflow -> {
buttonStyle = DownloadButton.ButtonStyle.NORMAL
setText(R.string.resume)
@ -428,6 +429,7 @@ object DownloadItemUtils {
DownloadStatus.pause,
DownloadStatus.timeout,
DownloadStatus.neterror,
DownloadStatus.diskisfull,
DownloadStatus.subscribe,
DownloadStatus.overflow -> {
if (isMultiVersion) {

View File

@ -98,18 +98,19 @@ object DownloadNotificationHelper {
DownloadStatus.waiting -> builder.setContentText("等待中")
DownloadStatus.subscribe,
DownloadStatus.timeout,
DownloadStatus.diskisfull,
DownloadStatus.neterror -> builder.setContentText("已暂停连接WiFi自动下载")
else -> builder.setContentText("暂停中")
}
builder.setProgress(PROGRESS_MAX, entity.percent.toInt(), false)
}
when {
entity.status == DownloadStatus.done -> {
when (entity.status) {
DownloadStatus.done -> {
builder.setSortKey("A")
builder.setOngoing(true) // 垃圾华为 sortKey 不起效 priority 也不起效,要将下载完成任务的通知置顶只能设置为 ongoing喷了
}
entity.status == DownloadStatus.downloading -> builder.setSortKey("B")
DownloadStatus.downloading -> builder.setSortKey("B")
else -> builder.setSortKey("C")
}

View File

@ -84,7 +84,9 @@ object DownloadObserver {
)
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
return
} else if (DownloadStatus.neterror == downloadEntity.status || DownloadStatus.timeout == downloadEntity.status) {
} else if (DownloadStatus.neterror == downloadEntity.status
|| DownloadStatus.timeout == downloadEntity.status
|| DownloadStatus.diskisfull == downloadEntity.status) {
if (downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD].isNullOrEmpty()
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)
) {
@ -95,7 +97,12 @@ object DownloadObserver {
Utils.log("DownloadObserver", "下载重试->" + downloadEntity.toJson())
}
} else {
Utils.toast(mApplication, "网络不稳定,下载任务已暂停")
if (DownloadStatus.diskisfull == downloadEntity.status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
} else {
ToastUtils.toast("网络不稳定,下载任务已暂停")
}
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
debugOnly {

View File

@ -1,16 +1,17 @@
package com.gh.common.xapk
import android.content.Context
import com.gh.gamecenter.core.AppExecutor
import com.gh.common.util.*
import com.gh.common.util.DownloadNotificationHelper
import com.gh.common.util.PackageInstaller
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.utils.debugOnly
import com.gh.gamecenter.common.utils.getExtension
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SentryHelper
import com.gh.ndownload.NDataChanger
import com.halo.assistant.HaloApp
import com.lightgame.download.DataChanger
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
import java.text.DecimalFormat
@ -81,7 +82,7 @@ object XapkInstaller : IXapkUnzipListener {
}
downloadEntity.meta[XAPK_UNZIP_PERCENT] = percent.toString()
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.UNZIPPING.name
DataChanger.notifyDataChanged(downloadEntity)
NDataChanger.notifyDataChanged(downloadEntity)
}
debugOnly {
@ -111,7 +112,7 @@ object XapkInstaller : IXapkUnzipListener {
AppExecutor.uiExecutor.execute {
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name
DataChanger.notifyDataChanged(downloadEntity)
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
}
}
@ -122,7 +123,7 @@ object XapkInstaller : IXapkUnzipListener {
AppExecutor.uiExecutor.execute {
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.FAILURE.name
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
DataChanger.notifyDataChanged(downloadEntity)
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
}
@ -156,7 +157,7 @@ object XapkInstaller : IXapkUnzipListener {
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.SUCCESS.name
DataChanger.notifyDataChanged(downloadEntity)
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
}

View File

@ -13,8 +13,8 @@ import com.gh.gamecenter.common.utils.getExtension
import com.gh.gamecenter.common.utils.getMetaExtra
import com.gh.gamecenter.common.utils.isSimulatorGame
import com.gh.gamecenter.core.utils.SentryHelper
import com.gh.ndownload.NDataChanger
import com.halo.assistant.HaloApp
import com.lightgame.download.DataChanger
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
@ -128,6 +128,7 @@ object DownloadDataHelper {
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
@ -202,6 +203,8 @@ object DownloadDataHelper {
// payload
val payloadObject = JSONObject()
val parallel = downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt()
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
@ -210,7 +213,10 @@ object DownloadDataHelper {
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("launch_ms", startupTime)
payloadObject.put("parallel", downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt() ?: 0)
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
if (parallel != null) {
payloadObject.put("parallel", parallel)
}
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
@ -234,7 +240,9 @@ object DownloadDataHelper {
// payload
val payloadObject = JSONObject()
val parallel = downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt()
val sizeInMB = downloadEntity.size / 1024 / 1024
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
@ -243,7 +251,9 @@ object DownloadDataHelper {
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("total_size", sizeInMB)
payloadObject.put("parallel", downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt() ?: 0)
if (parallel != null) {
payloadObject.put("parallel", parallel)
}
if (statusAlias == "下载完成") {
val elapsedTimeString = downloadEntity.meta[DownloadConfig.KEY_DOWNLOAD_ELAPSED_TIME]
@ -264,7 +274,7 @@ object DownloadDataHelper {
payloadObject.put("speed", speed)
}
} else {
payloadObject.put("task_num", DataChanger.downloadingTasks.size)
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
}
payloadObject.put("completed_size", downloadEntity.progress / 1024 / 1024)
if (downloadEntity.status == DownloadStatus.resume) {
@ -301,6 +311,8 @@ object DownloadDataHelper {
// payload
val payloadObject = JSONObject()
val parallel = downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt()
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
@ -311,8 +323,10 @@ object DownloadDataHelper {
payloadObject.put("speed_progress", JSONArray(averageSpeedList))
payloadObject.put("is_finished", downloadEntity.status == DownloadStatus.done)
payloadObject.put("completed_size", downloadEntity.progress / 1024 / 1024)
payloadObject.put("parallel", downloadEntity.meta[DOWNLOAD_THREAD_SIZE]?.toInt() ?: 0)
payloadObject.put("task_num", DataChanger.downloadingTasks.size)
if (parallel != null) {
payloadObject.put("parallel", parallel)
}
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
@ -335,7 +349,7 @@ object DownloadDataHelper {
* 在后台唤醒的情况下 下载状态可能无法修正
* see [DownloadManager.initDownloadService]
*/
if (downloadEntity.status == DownloadStatus.downloading && DataChanger.downloadingTasks[downloadEntity.url] != null) {
if (downloadEntity.status == DownloadStatus.downloading && NDataChanger.downloadingTasks[downloadEntity.url] != null) {
var sheet = mDownloadHeartbeatSheet[downloadEntity.url]
if (sheet == null) {
sheet = JSONObject()

View File

@ -47,17 +47,17 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.manager.PackagesManager;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.packagehelper.PackageRepository;
import com.gh.ndownload.NDataChanger;
import com.gh.ndownload.NDownloadBridge;
import com.gh.ndownload.NDownloadService;
import com.halo.assistant.HaloApp;
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;
@ -91,7 +91,7 @@ public class DownloadManager implements DownloadStatusListener {
private final ArrayMap<String, DownloadStatus> statusMap;
private final ArrayMap<String, DownloadEntity> downloadingMap;
private ArrayList<DownloadEntity> mInvisiblePendingTaskList; // 用户不可见的 pending 任务
private final ArrayList<DownloadEntity> mInvisiblePendingTaskList; // 用户不可见的 pending 任务
private final DownloadDao mDownloadDao;
private final DownloadedGameIdAndPackageNameDao mDownloadedGameIdAndPackageNameDao;
@ -167,8 +167,6 @@ public class DownloadManager implements DownloadStatusListener {
mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK);
DownloadStatusManager.getInstance().registerTaskStatusListener(this);
// 只有下载模块需要这坨东西,因此移动到这里初始化
ConnectionUtils.initHttpsUrlConnection(mContext);
@ -418,7 +416,7 @@ public class DownloadManager implements DownloadStatusListener {
if (isDownloadCompleted(url)) {
downloadEntity.setStatus(DownloadStatus.done);
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
} else if (!isTaskDownloading(url)) {
startDownloadService(downloadEntity, DownloadStatus.add);
}
@ -439,7 +437,7 @@ public class DownloadManager implements DownloadStatusListener {
checkDownloadEntryRecordValidate(url);
if (isDownloadCompleted(url)) {
downloadEntity.setStatus(DownloadStatus.done);
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
} else if (!isTaskDownloading(url)) {
DownloadEntity daoEntity = mDownloadDao.get(downloadEntity.getUrl());
if (automatic) {
@ -474,7 +472,7 @@ public class DownloadManager implements DownloadStatusListener {
checkDownloadEntryRecordValidate(url);
if (isDownloadCompleted(url)) {
downloadEntity.setStatus(DownloadStatus.done);
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
} else if (!isTaskDownloading(url)) {
startDownloadService(downloadEntity, DownloadStatus.subscribe);
}
@ -520,7 +518,7 @@ public class DownloadManager implements DownloadStatusListener {
* 任务是否已经下载中
*/
public boolean isTaskDownloading(String url) {
if (DataChanger.INSTANCE.getDownloadingTasks().get(url) != null) {
if (NDataChanger.INSTANCE.getDownloadingTasks().get(url) != null) {
Utils.log(DownloadManager.class.getSimpleName(), url + "正在下载!");
return true;
}
@ -528,7 +526,7 @@ public class DownloadManager implements DownloadStatusListener {
}
private Intent getIntent(DownloadEntity entry, DownloadStatus status) {
Intent service = new Intent(mContext, DownloadService.class);
Intent service = new Intent(mContext, NDownloadService.class);
service.putExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY, entry);
service.putExtra(DownloadConfig.KEY_DOWNLOAD_ACTION, status.name());
return service;
@ -557,6 +555,16 @@ public class DownloadManager implements DownloadStatusListener {
return mDownloadDao.getAllSnapshots();
}
/**
* 获取快照
*
* @param url 下载地址
*/
@Nullable
public DownloadEntity getDownloadEntitySnapshot(String url) {
return mDownloadDao.getSnapshot(url);
}
/**
* 获取快照
*
@ -797,6 +805,8 @@ public class DownloadManager implements DownloadStatusListener {
DownloadEntity entry = mDownloadDao.getSnapshot(url);
if (entry != null) {
AppExecutor.getIoExecutor().execute(() -> {
NDownloadBridge.INSTANCE.cancel(url);
mDownloadDao.delete(url);
if (isDeleteFile) {
@ -830,19 +840,16 @@ public class DownloadManager implements DownloadStatusListener {
private void cancelAndNotify(DownloadEntity entry, boolean cancelSilently) {
mDownloadDao.removeErrorMessage(entry.getUrl());
DownloadTask task = DataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl());
DownloadTask task = NDataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl());
if (task != null) {
task.cancel();
// 改任务队列的状态
DataChanger.INSTANCE.getDownloadingTasks().remove(entry.getUrl());
if (!cancelSilently) {
DataChanger.INSTANCE.notifyDataChanged(entry);
}
NDataChanger.INSTANCE.getDownloadingTasks().remove(entry.getUrl());
}
DataChanger.INSTANCE.getDownloadEntries().remove(entry.getUrl());
NDataChanger.INSTANCE.getDownloadEntries().remove(entry.getUrl());
if (!cancelSilently) {
DataChanger.INSTANCE.notifyDataChanged(entry);
DownloadStatusManager.getInstance().onTaskCancelled(entry);
NDataChanger.INSTANCE.notifyDataChanged(entry);
onTaskCancelled(entry);
}
Utils.log(DownloadManager.class.getSimpleName(), "cancel");
@ -852,7 +859,7 @@ public class DownloadManager implements DownloadStatusListener {
* 暂停所有正在下载的任务
*/
public void pauseAll() {
for (DownloadEntity entity : DataChanger.INSTANCE.getDownloadEntries().values()) {
for (DownloadEntity entity : NDataChanger.INSTANCE.getDownloadEntries().values()) {
pause(entity.getUrl());
}
Utils.log(DownloadManager.class.getSimpleName(), "pause all");
@ -863,7 +870,7 @@ public class DownloadManager implements DownloadStatusListener {
*/
public void pause(String url) {
checkDownloadEntryRecordValidate(url);
DownloadEntity entry = DataChanger.INSTANCE.getDownloadEntries().get(url);
DownloadEntity entry = NDataChanger.INSTANCE.getDownloadEntries().get(url);
if (entry != null) {
startDownloadService(entry, DownloadStatus.pause);
put(url, System.currentTimeMillis());
@ -879,14 +886,14 @@ public class DownloadManager implements DownloadStatusListener {
* 3.检查是否显示下载通知栏
*/
public void initDownloadService() {
final List<String> urlList = new ArrayList<>(DataChanger.INSTANCE.getDownloadingTasks().keySet());
final List<String> urlList = new ArrayList<>(NDataChanger.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);
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
}
}
@ -899,7 +906,7 @@ public class DownloadManager implements DownloadStatusListener {
*/
public void addObserver(DataWatcher dataWatcher) {
Utils.log(DownloadManager.class.getSimpleName(), "addObserver");
DataChanger.INSTANCE.addObserver(dataWatcher);
NDataChanger.INSTANCE.addObserver(dataWatcher);
notifyDownloadStatusASAP(dataWatcher);
}
@ -909,7 +916,7 @@ public class DownloadManager implements DownloadStatusListener {
*/
public void removeObserver(DataWatcher dataWatcher) {
Utils.log(DownloadManager.class.getSimpleName(), "removeObserver");
DataChanger.INSTANCE.deleteObserver(dataWatcher);
NDataChanger.INSTANCE.deleteObserver(dataWatcher);
}
/**
@ -925,11 +932,11 @@ public class DownloadManager implements DownloadStatusListener {
* 初始化下载服务
*/
public void startDownloadService() {
Intent serviceIntent = new Intent(mContext, DownloadService.class);
Intent serviceIntent = new Intent(mContext, NDownloadService.class);
// 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& !PackageUtils.isAppOnForeground(mContext)) {
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
serviceIntent.putExtra(NDownloadService.KEY_SERVICE_ACTION, NDownloadService.START_FOREGROUND);
mContext.startForegroundService(serviceIntent);
} else {
/*
@ -969,7 +976,7 @@ public class DownloadManager implements DownloadStatusListener {
// 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& !PackageUtils.isAppOnForeground(mContext)) {
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
serviceIntent.putExtra(NDownloadService.KEY_SERVICE_ACTION, NDownloadService.START_FOREGROUND);
mContext.startForegroundService(serviceIntent);
} else {
mContext.startService(serviceIntent);
@ -1100,7 +1107,7 @@ public class DownloadManager implements DownloadStatusListener {
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);
mDownloadDao.update(downloadEntity, false);
if (!markHasChanged) markHasChanged = true;
}
}
@ -1140,12 +1147,12 @@ public class DownloadManager implements DownloadStatusListener {
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);
mDownloadDao.update(downloadEntity, false);
if (!markHasChanged) markHasChanged = true;
}
} else {
downloadEntity.getMeta().put(DOWNLOADING_IS_READ_MARK, "");
mDownloadDao.newOrUpdate(downloadEntity);
mDownloadDao.update(downloadEntity, false);
if (!markHasChanged) markHasChanged = true;
}
}
@ -1203,7 +1210,7 @@ public class DownloadManager implements DownloadStatusListener {
* 更新数据库中的下载实体
*/
public void updateDownloadEntity(DownloadEntity downloadEntity) {
mDownloadDao.newOrUpdate(downloadEntity);
mDownloadDao.update(downloadEntity, false);
}
/**

View File

@ -83,10 +83,6 @@ object DownloadMessageHandler : InnerDownloadListener {
}
}
override fun onDownloadComplete(id: String?, elapsedTime: Long) {
// do nothing
}
/**
* 重定向中的回调
* @param id 下载 id
@ -218,6 +214,10 @@ object DownloadMessageHandler : InnerDownloadListener {
// do nothing
}
override fun onDownloadComplete(id: String?, elapsedTime: Long) {
// do nothing
}
fun registerListener(id: String, listener: DownloadListener) {
var listenerList = mListenerMap[id]
if (listenerList == null) {

View File

@ -61,6 +61,9 @@ import com.gh.common.util.ErrorHelper;
import com.gh.common.util.HomePluggableHelper;
import com.gh.common.util.LogUtils;
import com.gh.common.util.LunchType;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.common.utils.NotificationHelper;
import com.gh.common.util.NewFlatLogUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
@ -581,7 +584,32 @@ public class MainActivity extends BaseActivity {
AppExecutor.getIoExecutor().execute(() -> {
UpdateManager updateManager = UpdateManager.getInstance(this);
updateManager.checkUpdate(true, null);
updateManager.setDismissCallback(mMainWrapperFragment::getDialog);
updateManager.setDismissCallback(() -> {
if (!EnvHelper.isBeta()) {
mMainWrapperFragment.getDialog();
} else {
UpdateManager.checkBetaUpdate(shouldUpdate -> {
if (shouldUpdate && !this.isFinishing()) {
DialogHelper.showCenterWarningDialog(this,
"更新提醒",
"您当前使用的光环内测版有新版本,是否更新?",
"取消",
"确定",
() -> {
DirectUtils.directToGameDetail(this, Constants.GHZS_BETA_GAME_ID, "", true, "desc", null);
return null;
},
() -> {
mMainWrapperFragment.getDialog();
return null;
});
} else {
mMainWrapperFragment.getDialog();
}
});
}
}
);
});
}

View File

@ -105,6 +105,8 @@ class SplashScreenActivity : BaseActivity() {
// mStartMainActivityDirectly = true;
SPUtils.setLong(Constants.SP_INITIAL_USAGE_TIME, System.currentTimeMillis())
HaloApp.getInstance().isBrandNewInstall = true
executeDex2OatInAdvance()
if (!PackageFlavorHelper.IS_TEST_FLAVOR) {
showPrivacyDialog(guideLayout)
} else {

View File

@ -161,7 +161,8 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi
adapter.notifyItemChanged(adapter.getBase() + location + 1);
}
}
if (downloadEntity.getStatus() == DownloadStatus.neterror) {
if (downloadEntity.getStatus() == DownloadStatus.neterror
|| downloadEntity.getStatus() == DownloadStatus.diskisfull) {
adapter.notifyItemChanged(adapter.getBase());
}
} else {

View File

@ -225,6 +225,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
} else if (status.equals(DownloadStatus.pause)
|| status.equals(DownloadStatus.timeout)
|| status.equals(DownloadStatus.neterror)
|| status.equals(DownloadStatus.diskisfull)
|| status.equals(DownloadStatus.subscribe)) {
viewHolder.binding.dmItemTvStartorpause.setText(R.string.resume);
viewHolder.binding.dmItemTvStartorpause.setButtonStyle(DownloadButton.ButtonStyle.NORMAL);
@ -234,6 +235,8 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
|| status.equals(DownloadStatus.neterror)
|| status.equals(DownloadStatus.subscribe)) {
viewHolder.binding.dmItemTvSpeed.setText("等待WIFI");
} else if (status.equals(DownloadStatus.diskisfull)) {
viewHolder.binding.dmItemTvSpeed.setText("已暂停,磁盘空间不足");
} else {
viewHolder.binding.dmItemTvSpeed.setText("已暂停");
}

View File

@ -1,5 +1,7 @@
package com.gh.gamecenter.manager;
import static com.gh.gamecenter.common.utils.ExtensionsKt.observableToMain;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
@ -16,13 +18,17 @@ import android.view.Window;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.NotificationManagerCompat;
import com.g00fy2.versioncompare.Version;
import com.gh.gamecenter.common.callback.SimpleCallback;
import com.gh.gamecenter.core.utils.CurrentActivityHolder;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.common.exposure.ExposureUtils;
import com.gh.gamecenter.common.utils.DataLogUtils;
@ -47,8 +53,8 @@ import com.gh.gamecenter.entity.AppEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.gh.ndownload.NDataChanger;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DataChanger;
import com.lightgame.download.DataWatcher;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
@ -158,6 +164,8 @@ public class UpdateManager {
}
} else if (DownloadStatus.neterror.equals(downloadEntity.getStatus())) {
Utils.toast(mApplicationContext, "网络错误,请稍后重试");
} else if (DownloadStatus.diskisfull.equals(downloadEntity.getStatus())) {
Utils.toast(mApplicationContext, "磁盘已满,请清理后重试");
} else if (DownloadStatus.timeout.equals(downloadEntity.getStatus())) {
Utils.toast(mApplicationContext, "请求超时,请稍后重试");
} else if (DownloadStatus.notfound.equals(downloadEntity.getStatus())) {
@ -215,7 +223,17 @@ public class UpdateManager {
loadingDialog = DialogUtils.showWaitDialog(mContext, "检查更新中...");
}
String channel = HaloApp.getInstance().getChannel();
RetrofitManager.getInstance().getApi().getUpdate(PackageUtils.getGhVersionName(), PackageUtils.getGhVersionCode(), channel)
String ghVersionName = PackageUtils.getGhVersionName();
// beta 版的光环在获取更新时等同于移动官网渠道的普通包
if (ghVersionName.contains("beta")) {
try {
ghVersionName = ghVersionName.split("-")[0];
channel = "GH_206";
} catch (Exception e) {
e.printStackTrace();
}
}
RetrofitManager.getInstance().getApi().getUpdate(ghVersionName, PackageUtils.getGhVersionCode(), channel)
.map(appEntity -> {
boolean isShowUpdateDialog = false;
@ -479,7 +497,7 @@ public class UpdateManager {
if (!isSilentUpdate
&& DownloadManager.getInstance().isTaskDownloading(appEntity.getUrl())) {
try {
DownloadEntity entity = DataChanger.INSTANCE.getDownloadEntries().get(appEntity.getUrl());
DownloadEntity entity = NDataChanger.INSTANCE.getDownloadEntries().get(appEntity.getUrl());
if (entity != null) {
ExtensionsKt.addMetaExtra(entity, Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新");
@ -584,6 +602,37 @@ public class UpdateManager {
}
}
/**
* 检查是否需要显示内测版更新
*
* @param callback 回调 true 时表示需要显示更新
*/
public static void checkBetaUpdate(SimpleCallback<Boolean> callback) {
RetrofitManager.getInstance().getApi()
.getGameDigest(Constants.GHZS_BETA_GAME_ID)
.compose(observableToMain())
.subscribe(new Response<GameEntity>() {
@Override
public void onResponse(@Nullable GameEntity response) {
super.onResponse(response);
if (response != null && response.getApk().size() > 0) {
ApkEntity apk = response.getApk().get(0);
Version ghVersion = new Version(PackageUtils.getGhVersionName());
boolean shouldShowBetaUpdate = ghVersion.isLowerThan(new Version(apk.getVersion()));
callback.onCallback(shouldShowBetaUpdate);
} else {
callback.onCallback(false);
}
}
@Override
public void onFailure(@Nullable HttpException e) {
super.onFailure(e);
callback.onCallback(false);
}
});
}
private String getUpdateOnceOnlySpKey() {
return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode();
}

View File

@ -530,7 +530,6 @@ class HaloPersonalFragment : BaseLazyFragment() {
mBinding.stub.inflate()
mStubBinding.statusBar.goneIf(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
mStubBinding.darkModeIv.goneIf(!(Config.getNightModeSetting()?.icon ?: false))
mStubBinding.darkModeIv.setImageResource(if (mIsDarkModeOn) R.drawable.ic_personal_light_mode else R.drawable.ic_personal_dark_mode)
mStubBinding.ivArrow.enlargeTouchArea()

View File

@ -0,0 +1,37 @@
package com.gh.ndownload
import com.gh.gamecenter.core.runOnUiThread
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadTask
import java.util.*
/**
* 下载进度变化通知
*/
object NDataChanger : Observable() {
val downloadingTasks: MutableMap<String, DownloadTask> = Collections.synchronizedMap(HashMap())//当前正在下载的任务队列
val downloadEntries: MutableMap<String, DownloadEntity> = Collections.synchronizedMap(HashMap())//包含所有下载任务的任务队列
@Synchronized
fun notifyDataChanged(entry: DownloadEntity?) {
runOnUiThread {
if (entry != null) {
if (downloadEntries[entry.url] != null) {
downloadEntries[entry.url] = entry
}
setChanged()
notifyObservers(entry)
}
}
}
@Synchronized
fun pauseDownloadingTask(url: String) {
runOnUiThread {
val downloadTask = downloadingTasks.remove(url)
downloadTask?.pause()
}
}
}

View File

@ -0,0 +1,409 @@
package com.gh.ndownload
import android.text.TextUtils
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.utils.NetworkUtils
import com.gh.gamecenter.common.utils.addMetaExtra
import com.gh.gamecenter.common.utils.deepClone
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.ToastUtils
import com.halo.assistant.HaloApp
import com.lg.download.DownloadError
import com.lg.download.listener.InnerDownloadListener
import com.lg.ndownload.*
import com.lightgame.download.DownloadDao
import com.lightgame.download.DownloadEntity
import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import java.math.RoundingMode
import java.net.URL
import java.net.URLConnection
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* 是串联丑陋的下载模块与新下载模块之间的桥梁
*/
object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
private const val TAG = "NDownloadBridge"
private const val DEFAULT_DOWNLOAD_THREAD_SIZE = 2
private const val NO_NETWORK_MAX_RETRY_COUNT = 5
private var mRetryTimer: Timer? = null // 用于错误重试的 timer
private val mRetryCountMap: ConcurrentHashMap<String, Int> by lazy { ConcurrentHashMap() }
private val mStartUpTimeMap: ConcurrentHashMap<String, Long> by lazy { ConcurrentHashMap() }
init {
DownloadQueue.getInstance().setMaxDownloadingTask(3)
}
override fun onError(id: String, error: DownloadError?, exception: Exception?) {
Utils.log(TAG, "id $id encounter $error -> ${exception?.message}")
resetRetryCount(id)
getDownloadEntity(id)?.let { downloadEntity ->
when (error) {
DownloadError.EMPTY_URL,
DownloadError.HTTP_NOT_FOUND,
DownloadError.CONTENT_LENGTH_IS_ZERO -> {
doCancel(downloadEntity)
downloadEntity.status = com.lightgame.download.DownloadStatus.notfound
}
DownloadError.HIJACKED -> {
doCancel(downloadEntity)
downloadEntity.status = com.lightgame.download.DownloadStatus.hijack
}
DownloadError.DOWNLOAD_SIZE_NOT_MATCH_CONTENT_LENGTH -> {
doCancel(downloadEntity)
downloadEntity.status = com.lightgame.download.DownloadStatus.overflow
DownloadManager.getInstance().onTaskError(downloadEntity)
}
DownloadError.PREVIOUS_DOWNLOAD_IS_DELETED,
DownloadError.FILE_CORRUPTED -> {
doCancel(downloadEntity)
ToastUtils.toast("文件已损坏,请重新下载")
downloadEntity.status = com.lightgame.download.DownloadStatus.cancel
}
DownloadError.PRE_DOWNLOAD_INTERNAL_ERROR,
DownloadError.DOWNLOAD_CONNECTION_ERROR,
DownloadError.PRE_DOWNLOAD_CONNECTION_ERROR -> {
downloadEntity.status = com.lightgame.download.DownloadStatus.neterror
DownloadManager.getInstance().onTaskError(downloadEntity)
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, true)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
DownloadError.DISK_IS_FULL -> {
downloadEntity.status = com.lightgame.download.DownloadStatus.diskisfull
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, true)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
DownloadError.RESOURCE_IS_FORBIDDEN -> {
val errorString = exception?.message
if (!errorString.isNullOrEmpty()) {
handleForbiddenException(downloadEntity, errorString)
}
doCancel(downloadEntity)
}
DownloadError.PERSONAL_NO_CERTIFICATION -> {
doCancel(downloadEntity)
downloadEntity.status = com.lightgame.download.DownloadStatus.uncertificated
}
DownloadError.PERSONAL_TEENAGER -> {
doCancel(downloadEntity)
downloadEntity.status = com.lightgame.download.DownloadStatus.unqualified
}
else -> {
// do nothing
}
}
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onProgress(id: String?, progress: Float) {
getDownloadEntity(id)?.let { downloadEntity ->
var percent = (progress * 100.0)
val df = DecimalFormat("#.#", DecimalFormatSymbols(Locale.CHINA))
df.roundingMode = RoundingMode.FLOOR
percent = df.format(percent).toDouble()
downloadEntity.progress = (downloadEntity.size * progress).toLong()
downloadEntity.percent = percent
// 有可能还处于 redirected 状态,这里检查并置换状态
if (com.lightgame.download.DownloadStatus.downloading != downloadEntity.status) {
downloadEntity.status = com.lightgame.download.DownloadStatus.downloading
}
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onProgressWithoutThrottle(id: String?, progress: Float) {
if (id != null && (mStartUpTimeMap[id] ?: 0) > 0) {
val startupTime: Long = System.currentTimeMillis() - (mStartUpTimeMap[id] ?: 0)
getDownloadEntity(id)?.let { downloadEntity ->
downloadEntity.meta.put(DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY, startupTime.toString())
}
mStartUpTimeMap[id] = 0
}
}
override fun onSizeReceived(id: String?, fileSize: Long) {
Utils.log(TAG, "id $id onSizeReceived $fileSize")
getDownloadEntity(id)?.let { downloadEntity ->
downloadEntity.size = fileSize
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
}
override fun onReadyToDownload(id: String?, actualThreadSize: Int) {
Utils.log(TAG, "id $id onReadyToDownload, actualThreadSize is -> $actualThreadSize")
getDownloadEntity(id)?.let { downloadEntity ->
downloadEntity.meta[DownloadDataHelper.DOWNLOAD_THREAD_SIZE] = actualThreadSize.toString()
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
}
override fun onStatusChanged(id: String, status: com.lg.download.DownloadStatus?) {
Utils.log(TAG, "id $id onStatusChanged $status")
resetRetryCount(id)
getDownloadEntity(id)?.let { originDownloadEntity ->
// 状态更新使用全新的拷贝对象,避免因为不同线程对数据的变更造成通知错乱
val downloadEntity = originDownloadEntity.deepClone<DownloadEntity>() ?: return@let
when (status) {
com.lg.download.DownloadStatus.CANCELLED -> {
doCancel(downloadEntity)
DownloadManager.getInstance().onTaskPaused(downloadEntity)
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, true)
}
com.lg.download.DownloadStatus.PAUSED -> {
downloadEntity.speed = 0
downloadEntity.status = com.lightgame.download.DownloadStatus.pause
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, false)
}
com.lg.download.DownloadStatus.WAITINGWIFI -> {
downloadEntity.status = com.lightgame.download.DownloadStatus.waiting
}
com.lg.download.DownloadStatus.DOWNLOADING -> {
downloadEntity.status = com.lightgame.download.DownloadStatus.downloading
DownloadManager.getInstance().onTaskAdded(downloadEntity)
}
com.lg.download.DownloadStatus.QUEUED -> {
downloadEntity.status = com.lightgame.download.DownloadStatus.add
}
else -> {
Utils.log(TAG, "status not supported yet -> $status")
}
}
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onDownloadComplete(id: String?, elapsedTime: Long) {
getDownloadEntity(id)?.let { originDownloadEntity ->
val downloadEntity = originDownloadEntity.deepClone<DownloadEntity>() ?: return@let
downloadEntity.speed = 0
downloadEntity.progress = downloadEntity.size
downloadEntity.percent = 100.0
downloadEntity.end = System.currentTimeMillis()
downloadEntity.status = com.lightgame.download.DownloadStatus.done
downloadEntity.addMetaExtra(com.lightgame.download.DownloadConfig.KEY_DOWNLOAD_ELAPSED_TIME, elapsedTime.toString())
DownloadManager.getInstance().onTaskDone(downloadEntity)
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, true)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onSpeedChanged(id: String, speed: Float) {
getDownloadEntity(id)?.let { downloadEntity ->
downloadEntity.speed = speed.toLong()
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onRedirectingUrl(id: String, connection: URLConnection?, config: DownloadConfig?) {
getDownloadEntity(id)?.let { downloadEntity ->
if (!TextUtils.isEmpty(downloadEntity.eTag)) {
val eTag: String = getRealETag(connection?.getHeaderField("Halo-ETag") ?: "")
downloadEntity.haloEtag = eTag
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun onRedirectedUrl(id: String, connection: URLConnection?, redirectedUrl: String?) {
getDownloadEntity(id)?.let { downloadEntity ->
downloadEntity.finalRedirectedUrl = redirectedUrl
val url = URL(redirectedUrl)
if (url.host != null && url.path != null) {
downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] = url.host
downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] = url.path
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
downloadEntity.status = com.lightgame.download.DownloadStatus.redirected
// downloadEntity 的 etag 的值用来判断是否需要进行劫持校验,值不参与到最后判断是否被劫持的校验中去
val shouldCompareETag = !TextUtils.isEmpty(downloadEntity.eTag)
val currentETag = connection?.getHeaderField("ETag")
if (shouldCompareETag
&& !TextUtils.isEmpty(currentETag)
&& currentETag != downloadEntity.haloEtag
) {
// 链接被劫持,抛出异常
downloadEntity.status = com.lightgame.download.DownloadStatus.hijack
}
NDataChanger.notifyDataChanged(downloadEntity)
}
}
override fun getRetryOption(id: String, e: Exception?): Int {
val downloadEntity =
DownloadDao.getInstance(HaloApp.getInstance()).getSnapshot(id) ?: return IErrorRetryHandler.RETRY_DIRECTLY
return if (e is IOException && FileUtils.getFreeSpaceByPath(downloadEntity.path) < 10F) {
IErrorRetryHandler.REFUSE_TO_RETRY
} else if (NetworkUtils.isWifiConnected(HaloApp.getInstance()) || isDownloadViaTrafficAllowed(downloadEntity)) {
IErrorRetryHandler.RETRY_DIRECTLY
} else {
IErrorRetryHandler.RETRY_MANUALLY
}
}
override fun retryManually(id: String, retryRunnable: Runnable, pauseRunnable: Runnable) {
if (mRetryTimer == null) {
mRetryTimer = Timer()
}
Utils.log(TAG, "about to retry download manually")
mRetryTimer?.schedule(object : TimerTask() {
override fun run() {
if (NetworkUtils.isWifiConnected(HaloApp.getInstance())) {
resetRetryCount(id)
retryRunnable.run()
} else {
mRetryCountMap[id] = (mRetryCountMap[id] ?: 0) + 1
if (mRetryCountMap[id]!! <= NO_NETWORK_MAX_RETRY_COUNT) {
retryManually(id, retryRunnable, pauseRunnable)
} else {
resetRetryCount(id)
pauseRunnable.run()
}
}
}
}, GlobalDownloadConfig.ERROR_RETRY_INTERVAL)
}
private fun isDownloadViaTrafficAllowed(downloadEntity: DownloadEntity?): Boolean {
val mNetworkMobileStatus = "2G3G4G5G"
val networkStatus: String? = downloadEntity?.meta?.get(DownloadEntity.NETWORK_STATUS_KEY)
Utils.log("download network status$networkStatus")
return if (networkStatus == null) false else mNetworkMobileStatus.contains(networkStatus)
}
fun download(downloadEntity: DownloadEntity) {
val config = DownloadConfigBuilder()
.setUniqueId(downloadEntity.url)
.setFileName("")
.setUrl(downloadEntity.url)
.setPathToStore(downloadEntity.path)
.setHttpClient(NHttpClient())
.setDownloadThreadSize(DEFAULT_DOWNLOAD_THREAD_SIZE)
.setDownloadListener(NDownloadBridge)
.setErrorRetryHandler(this)
.setDownloadExecutor(AppExecutor.downloadExecutor)
.setMeta(downloadEntity.meta as HashMap<String, String>?)
.setErrorRetryHandler(this)
.build()
mStartUpTimeMap[downloadEntity.url] = System.currentTimeMillis()
DownloadQueue.getInstance().submitNewTask(config)
}
fun resume(id: String?) {
id?.let {
DownloadQueue.getInstance().resume(id)
getDownloadEntity(id)?.let { downloadEntity ->
mStartUpTimeMap[downloadEntity.url] = System.currentTimeMillis()
}
}
}
fun pause(id: String?) {
id?.let {
DownloadQueue.getInstance().pause(id)
}
}
fun cancel(id: String?) {
id?.let {
NDataChanger.downloadEntries.remove(id)
DownloadQueue.getInstance().cancel(id)
}
}
private fun doCancel(downloadEntity: DownloadEntity) {
DownloadManager.getInstance().cancel(downloadEntity.url)
}
private fun getRealETag(originETag: String): String {
var realETag = originETag
if (!TextUtils.isEmpty(realETag) && realETag.startsWith("\"") && realETag.endsWith("\"")) {
realETag = realETag.substring(1, realETag.length - 1)
}
return realETag
}
private fun handleForbiddenException(downloadEntity: DownloadEntity, errorString: String) {
try {
val resultObject = JSONObject(errorString)
if ("403001" == resultObject.getString("code")) {
// 未实名
if (resultObject.has("force") && !resultObject.getBoolean("force")) {
downloadEntity.meta["force_real_name"] = "false"
}
downloadEntity.status = com.lightgame.download.DownloadStatus.uncertificated
} else if ("403002" == resultObject.getString("code")) {
// 未成年
if (resultObject.has("force") && !resultObject.getBoolean("force")) {
downloadEntity.meta["force_real_name"] = "false"
}
downloadEntity.status = com.lightgame.download.DownloadStatus.unqualified
} else if ("403003" == resultObject.getString("code")) {
// 该游戏未接入防沉迷系统禁止下载
downloadEntity.status = com.lightgame.download.DownloadStatus.unavailable
} else if ("403004" == resultObject.getString("code")) {
// 后台禁止该设备下载
downloadEntity.status = com.lightgame.download.DownloadStatus.banned
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
private fun resetRetryCount(id: String) {
mRetryCountMap[id] = 0
}
private fun getDownloadEntity(id: String?): DownloadEntity? {
return DownloadManager.getInstance().getDownloadEntitySnapshot(id)
}
}

View File

@ -0,0 +1,276 @@
package com.gh.ndownload;
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.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.DownloadTask;
import com.lightgame.download.ForegroundNotificationManager;
import com.lightgame.utils.Utils;
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";
private static final String SERVICE_CHANNEL_ID = "Halo_Download";
private static NDownloadService sService = null;
private ForegroundNotificationManager mForegroundNotificationManager;
@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);
}
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 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();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
synchronized void subscribeDownload(DownloadEntity entry) {
NDataChanger.INSTANCE.getDownloadEntries().put(entry.getUrl(), entry);
DownloadDao.getInstance(this).newOrUpdate(entry);
NDataChanger.INSTANCE.notifyDataChanged(entry);
DownloadManager.getInstance().onTaskAdded(entry);
}
synchronized void addDownload(DownloadEntity entry) {
if (NDataChanger.INSTANCE.getDownloadingTasks().size() >= DownloadConfig.MAX_DOWNLOADING_SIZE) {
// 1.改任务队列的状态
entry.setStatus(DownloadStatus.waiting);
NDataChanger.INSTANCE.getDownloadEntries().put(entry.getUrl(), entry);
// 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) {
DownloadTask task = NDataChanger.INSTANCE.getDownloadingTasks()
.get(downloadEntity.getUrl());
if (task != null) {
NDownloadBridge.INSTANCE.cancel(downloadEntity.getUrl());
// 改任务队列的状态
NDataChanger.INSTANCE.getDownloadingTasks().remove(downloadEntity.getUrl());
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
}
NDataChanger.INSTANCE.getDownloadEntries().remove(downloadEntity.getUrl());
NDataChanger.INSTANCE.notifyDataChanged(downloadEntity);
DownloadManager.getInstance().onTaskCancelled(downloadEntity);
}
synchronized void startDownload(DownloadEntity entry) {
DownloadTask task = NDataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl());
if (task == null) {
// 刷新初始状态
NDataChanger.INSTANCE.notifyDataChanged(entry);
entry.getMeta().put(DownloadEntity.DOWNLOAD_STARTUP_STATUS_KEY, entry.getStatus().getStatus());
// 1.改任务队列的状态
entry.setStatus(DownloadStatus.downloading);
NDataChanger.INSTANCE.getDownloadEntries().put(entry.getUrl(), entry);
NDataChanger.INSTANCE.getDownloadingTasks().put(entry.getUrl(), new DownloadTask(null, entry, this));
// 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");
}
}
/**
* 从 DataChanger 里移除掉数据,并找到下一个处于 waiting 状态的任务继续
* @param downloadEntity 需要移除的数据
* @param removeCompletely 是否完全移除false 时只从下载中 hashMap 移除
*/
public synchronized void removeFromDataChangerAndResumeWaitingTask(DownloadEntity downloadEntity, boolean removeCompletely) {
NDataChanger.INSTANCE.getDownloadingTasks().remove(downloadEntity.getUrl());
if (removeCompletely) {
NDataChanger.INSTANCE.getDownloadEntries().remove(downloadEntity.getUrl());
}
for (DownloadEntity entry : NDataChanger.INSTANCE.getDownloadEntries().values()) {
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);
}
}
}
/**
* 获取 0 以外的随机 int 数值
*/
private int getNextIntExcludeZero() {
int randomNumber = new Random().nextInt();
if (randomNumber != 0) {
return randomNumber;
} else {
return getNextIntExcludeZero();
}
}
}

View File

@ -0,0 +1,31 @@
package com.gh.ndownload
import android.text.TextUtils
import com.lg.download.httpclient.DefaultHttpClient
import com.lightgame.download.HttpDnsManager
class NHttpClient : DefaultHttpClient() {
override fun addHeader(url: String?, meta: HashMap<String, String>?) {
super.addHeader(url, meta)
mConnection.setRequestProperty(HttpDnsManager.APP_VERSION, HttpDnsManager.metaMap[HttpDnsManager.APP_VERSION])
mConnection.setRequestProperty(HttpDnsManager.CHANNEL, HttpDnsManager.metaMap[HttpDnsManager.CHANNEL])
mConnection.setRequestProperty(HttpDnsManager.GID, HttpDnsManager.metaMap[HttpDnsManager.GID])
mConnection.setRequestProperty(HttpDnsManager.USER_ID, HttpDnsManager.metaMap[HttpDnsManager.USER_ID])
mConnection.setRequestProperty(HttpDnsManager.IMEI, HttpDnsManager.metaMap[HttpDnsManager.IMEI])
mConnection.setRequestProperty(HttpDnsManager.OAID, HttpDnsManager.metaMap[HttpDnsManager.OAID])
mConnection.setRequestProperty(HttpDnsManager.TOKEN, HttpDnsManager.metaMap[HttpDnsManager.TOKEN])
mConnection.setRequestProperty(HttpDnsManager.IS_OVERWRITE, HttpDnsManager.metaMap[HttpDnsManager.IS_OVERWRITE])
val isEmulator = meta?.get("is_emulator")
val isForcedRealName = meta?.get("force_real_name")
if (!TextUtils.isEmpty(isForcedRealName) && "false" == isForcedRealName) {
mConnection.setRequestProperty("force", isForcedRealName)
}
if (!TextUtils.isEmpty(isEmulator)) {
mConnection.setRequestProperty("simulator", isEmulator)
}
}
}

View File

@ -181,6 +181,7 @@ class HomeRecentVGameAdapter(context: Context) : DiffUtilAdapter<VGameItemData>(
DownloadStatus.uncertificated,
DownloadStatus.unqualified,
DownloadStatus.notfound,
DownloadStatus.diskisfull,
DownloadStatus.unavailable,
DownloadStatus.overflow -> {
binding.root.setOnClickListener {

View File

@ -101,7 +101,7 @@ object VArchiveHelper {
.setHttpClient(DefaultHttpClient())
.setDownloadThreadSize(2)
.setDownloadListener(DownloadMessageHandler)
.setDownloadExecutor(AppExecutor.ioExecutor)
.setDownloadExecutor(AppExecutor.downloadExecutor)
.build()
)
}

View File

@ -387,6 +387,7 @@ class VDownloadManagerAdapter(
DownloadStatus.timeout,
DownloadStatus.neterror,
DownloadStatus.subscribe,
DownloadStatus.diskisfull,
DownloadStatus.overflow -> {
btnText = context.getString(R.string.resume)
downloadDes.visibility = View.VISIBLE

View File

@ -55,6 +55,7 @@ data class VGameItemData(
DownloadStatus.cancel,
DownloadStatus.timeout,
DownloadStatus.neterror,
DownloadStatus.diskisfull,
DownloadStatus.hijack,
DownloadStatus.uncertificated,
DownloadStatus.unqualified,
@ -62,6 +63,8 @@ data class VGameItemData(
DownloadStatus.unavailable,
DownloadStatus.overflow -> {
controlText = "重试"
shouldShowMask = true
shouldShowControlTv = true
}
else -> {
// do nothing

View File

@ -181,6 +181,7 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
overflow,
timeout,
neterror,
diskisfull,
waiting,
subscribe -> {
downloadBtn.setText(R.string.waiting)

View File

@ -48,6 +48,7 @@ class VSpaceLoadingFragment : BaseFragment<Any>() {
hijack,
notfound,
neterror,
diskisfull,
overflow -> requireActivity().finish()
unqualified -> {
ToastUtils.toast("暂不支持未成年人下载")

View File

@ -69,6 +69,7 @@ import com.gh.gamecenter.receiver.NetworkStateReceiver;
import com.gh.vspace.VHelper;
import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
import com.lg.ndownload.DownloadDbManager;
import com.lightgame.utils.Utils;
import com.llew.huawei.verifier.LoadedApkHuaWei;
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
@ -218,6 +219,8 @@ public class HaloApp extends MultiDexApplication {
// 异步初始化 SP
SPUtils.getString("");
DownloadDbManager.init(this);
});
RxJavaPlugins.setIoSchedulerHandler(scheduler -> AppExecutor.INSTANCE.getCachedScheduler());

View File

@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:background="@color/background"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

View File

@ -29,7 +29,7 @@
android:background="@color/background"
android:gravity="center"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<include
android:id="@+id/reuseSearchBar"

View File

@ -48,7 +48,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/head_container"

View File

@ -18,7 +18,7 @@
android:background="@color/background_white"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.gh.gamecenter.common.view.ScrimAwareCollapsingToolbarLayout
android:id="@+id/collapsingToolbar"

View File

@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<LinearLayout
android:layout_width="match_parent"

View File

@ -20,7 +20,7 @@
android:background="@color/background"
android:fitsSystemWindows="true"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.gh.gamecenter.common.view.ScrimAwareCollapsingToolbarLayout
android:id="@+id/collapsingToolbar"

View File

@ -26,7 +26,7 @@
android:background="@color/background_white"
android:fitsSystemWindows="true"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"

View File

@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:background="@null"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/recordForumsContainer"

View File

@ -20,7 +20,7 @@
android:fitsSystemWindows="true"
android:gravity="center"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@id/collapsingToolbar"

View File

@ -19,7 +19,7 @@
android:background="@color/background_white"
android:gravity="center"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@id/collapsingToolbar"

View File

@ -18,7 +18,7 @@
android:background="@color/background_white"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.gh.gamecenter.common.view.ScrimAwareCollapsingToolbarLayout
android:id="@+id/collapsingToolbar"

View File

@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:background="@null"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.gh.common.view.CollapsingMotionLayout
android:id="@+id/motionLayout"

View File

@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"

View File

@ -13,7 +13,7 @@
android:layout_marginTop="8dp"
android:background="@color/background_white"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

View File

@ -22,7 +22,7 @@
android:background="@color/background"
android:gravity="center"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<include
android:id="@+id/search_bar"

View File

@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:background="@color/transparent"
app:elevation="0dp"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"

View File

@ -11,7 +11,7 @@
android:layout_height="wrap_content"
android:background="@color/background_white"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior">
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/subject_type_list"

View File

@ -14,7 +14,7 @@
android:background="@color/background_white"
android:fitsSystemWindows="true"
android:gravity="center"
app:layout_behavior=".common.view.FixAppBarLayoutBehavior"
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"
tools:visibility="visible">
<com.google.android.material.appbar.CollapsingToolbarLayout

View File

@ -65,6 +65,7 @@ public class Constants {
public static final String DOWNLOAD_ID = "download_id";
public static final String GHZS_GAME_ID = "5ae4462c2924bc7936438d07";
public static final String GHZS_BETA_GAME_ID = "61e7e41519041110ca121a11";
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
public static final String SILENT_UPDATE = "静默更新";

View File

@ -206,24 +206,25 @@ object MetaUtil {
?: return "unknown"
return when (activeNetwork.type) {
ConnectivityManager.TYPE_WIFI -> "Wifi"
ConnectivityManager.TYPE_WIMAX -> "WifiMax"
ConnectivityManager.TYPE_WIFI -> "WIFI"
ConnectivityManager.TYPE_WIMAX -> "WIMAX"
ConnectivityManager.TYPE_MOBILE -> {
val telephonyManager = application.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if (telephonyManager.simState != TelephonyManager.SIM_STATE_READY) return "unknown"
when (telephonyManager.networkType) {
// Unknown
TelephonyManager.NETWORK_TYPE_UNKNOWN -> "Cellular - Unknown"
TelephonyManager.NETWORK_TYPE_UNKNOWN -> "unknown"
// Cellular Data2G
TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA,
TelephonyManager.NETWORK_TYPE_IDEN, TelephonyManager.NETWORK_TYPE_1xRTT -> "Cellular - 2G"
TelephonyManager.NETWORK_TYPE_IDEN, TelephonyManager.NETWORK_TYPE_1xRTT -> "2G"
// Cellular Data3G
TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSPA,
TelephonyManager.NETWORK_TYPE_HSPAP, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_EVDO_0,
TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_EVDO_B -> "Cellular - 3G"
TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_EVDO_B -> "3G"
// Cellular Data4G
TelephonyManager.NETWORK_TYPE_LTE -> "Cellular - 4G"
else -> "Cellular - Unknown Generation"
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
else -> "unknown"
}
}

View File

@ -149,6 +149,9 @@ public class DeviceUtils {
case TelephonyManager.NETWORK_TYPE_LTE:
status = "4G";
break;
case TelephonyManager.NETWORK_TYPE_NR:
status = "5G";
break;
default:
status = "未知";
break;

View File

@ -2,7 +2,6 @@ package com.gh.gamecenter.common.utils
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.BuildConfig
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IBuildConfigProvider
import com.gh.gamecenter.core.utils.SPUtils
@ -15,6 +14,14 @@ object EnvHelper {
SPUtils.getBoolean(Constants.SP_IS_DEV_ENV, false)
}
// 当前包体是不是 beta (内测包)
@JvmStatic
val isBeta: Boolean by lazy {
val buildConfig =
ARouter.getInstance().build(RouteConsts.provider.buildConfig).navigation() as? IBuildConfigProvider
buildConfig?.getVersionName()?.contains("beta") ?: false
}
@JvmStatic
fun getHost(): String {
val buildConfig =

View File

@ -67,6 +67,7 @@ import okhttp3.MediaType
import okhttp3.RequestBody
import org.json.JSONArray
import org.json.JSONObject
import java.io.*
import java.net.URI
import java.util.*
import java.util.concurrent.TimeUnit
@ -411,6 +412,25 @@ fun Context.showRegulationTestDialogIfNeeded(action: (() -> Unit)) {
}
}
/**
* Serializable 相关
*/
fun <T> Serializable.deepClone(): T? {
Utils.log("深复制对象 $this")
return try {
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(this)
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
ois.readObject() as T
} catch (e: IOException) {
null
} catch (e: ClassNotFoundException) {
null
}
}
/**
* 在限定 interval 里只触发一次 action
*/

View File

@ -165,7 +165,7 @@ public class NetworkUtils {
return "3G";
case TelephonyManager.NETWORK_TYPE_LTE:
return "4G";
case TelephonyManager.NETWORK_TYPE_NR:
case TelephonyManager.NETWORK_TYPE_NR://暂未适配AndroidQ这里粗暴设置为20
return "5G";
default:
return "unknown";

View File

@ -17,6 +17,7 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IActivationProvider
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IDirectProvider
@ -102,12 +103,14 @@ object PermissionHelper {
.subscribe { permission ->
when {
permission.granted -> {
emptyCallback.onCallback()
SPUtils.setBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION, false)
// 获得存储权限后刷新 gid 避免下载仍需实名
(ARouter.getInstance().build(RouteConsts.provider.app)
.navigation() as? IAppProvider)?.refreshGid()
(ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider)?.refreshGid()
// 延迟 100ms 确保能够正常获取到 gid
AppExecutor.uiExecutor.executeWithDelay({
emptyCallback.onCallback()
}, 100)
SPUtils.setBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION, false)
}
permission.shouldShowRequestPermissionRationale -> {
// do nothing

View File

@ -59,6 +59,10 @@ object AppExecutor {
)
}
val downloadExecutor: ExecutorService by lazy {
Executors.newCachedThreadPool(GHThreadFactory("GH_DOWNLOAD_THREAD"))
}
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
class MainThreadExecutor : Executor {

View File

@ -208,20 +208,17 @@ class SettingsFragment : ToolbarFragment() {
}
}
mBinding.systemDarkModeItem.run {
val config = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
root.goneIf(!(config?.getNightModeSetting() ?: false)) {
titleTv.text = getString(R.string.setting_system_dark_mode)
switchLottie.visibility = View.VISIBLE
switchLottie.setSwitchAnimation(DarkModeUtils.isFollowSystemDarkModeFromSp())
root.setOnClickListener {
val status = DarkModeUtils.isFollowSystemDarkModeFromSp()
NewFlatLogUtils.logHaloSelfNightModeOsSwitch(!status)
switchLottie.setSwitchAnimation(status)
switchLottie.playAnimation()
DarkModeUtils.updateFollowSystemDarkModeToSp(!status)
DarkModeUtils.updateAppDarkModeStatusToSp(mIsDarkModeOn)
DarkModeUtils.initDarkMode()
}
titleTv.text = getString(R.string.setting_system_dark_mode)
switchLottie.visibility = View.VISIBLE
switchLottie.setSwitchAnimation(DarkModeUtils.isFollowSystemDarkModeFromSp())
root.setOnClickListener {
val status = DarkModeUtils.isFollowSystemDarkModeFromSp()
NewFlatLogUtils.logHaloSelfNightModeOsSwitch(!status)
switchLottie.setSwitchAnimation(status)
switchLottie.playAnimation()
DarkModeUtils.updateFollowSystemDarkModeToSp(!status)
DarkModeUtils.updateAppDarkModeStatusToSp(mIsDarkModeOn)
DarkModeUtils.initDarkMode()
}
}

32
scripts/build_beta_release.sh Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
# @author juntao
# @2021.11.30
git_sha=`git rev-parse --short HEAD`
versionCode=$(awk -v FS="versionCode = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g")
versionName=$(awk -v FS="versionName = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g")
build_time=$(TZ=Asia/Shanghai date +'%Y-%m%d-%H%M')
git checkout app/build.gradle
git checkout module_common/build.gradle
sed -i '1 a android.enableResourceOptimizations = false' gradle.properties
# 开启 mapping 上传
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' '1 a plugins { id "io.sentry.android.gradle" version "2.0.1" } ' app/build.gradle
else
sed -i '1 a plugins { id "io.sentry.android.gradle" version "2.0.1" }' app/build.gradle
fi
./gradlew --stop
./gradlew clean
sed -i '260 a implementation(project(\x27:module_setting_compose\x27)) { exclude group: \x27androidx.swiperefreshlayout\x27 }' app/build.gradle
./gradlew rPR -I init.compose.gradle
mkdir -p release-app/${versionName}_${versionCode}
cp -R app/build/outputs/apk/publish/release/app-publish-release.apk release-app/${versionName}_${versionCode}/光环助手内测版_${versionName}_${versionCode}_${git_sha}_${build_time}.apk
git checkout app/build.gradle
git checkout module_common/build.gradle