diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a0740cf999..063581b94f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,12 +1,57 @@ stages: - - analysis - - sendmail + - android-build && analysis + - docker-build && sendmail + - deploy-trigger -## 代码检查 +android_build: + tags: + - offline-test + stage: android-build && analysis + image: hub.shanqu.cc/devops/ci-android:jdk11 + variables: + KUBERNETES_CPU_LIMIT: "16" + GIT_SUBMODULE_STRATEGY: recursive + Apk_Path: "app/build/outputs/apk/**/release/*.apk" + script: + - export GRADLE_USER_HOME=./.gradle + - chmod +x ./gradlew + - ./scripts/meta_build.sh --config_id 6400549c21c2c94ead074500 --sdk_platform toutiao --sdk_version 5.3.0 --channel BD-GHZS-ZP-KY --activate_reporting_ratio 60 --first_lanuch_jump e1wibGlua190eXBlXCI6XCJcIixcImxpbmtfaWRcIjpcIlwiLFwibGlua190ZXh0XCI6XCJcIixcImhvbWVfaW5kZXhcIjpcIlwiLFwiYm90dG9tX2luZGV4XCI6XCJcIn0= --unix_timestamp 1677657618 --output ./release/com.gh.gamecenter_5.17.4_694_BD-GHZS-ZP-KY_toutiao_5.3.0_1677657618.apk + - rm -rf ./.gradle/caches/build-cache-1 + cache: + paths: + - .gradle + artifacts: + paths: + - Dockerfile + expire_in: 15 mins + only: + - feature-meta_build + +# 构建推送docker镜像 +docker-build: + tags: + - offline-test + stage: docker-build && sendmail + image: hub.shanqu.cc/library/docker:latest + variables: + GIT_STRATEGY: none + script: + - projectPath=`echo $CI_PROJECT_PATH | sed 's#/#-#g'` + - docker build -t registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest . + - docker push registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest + - docker run -e PROJECTKEY=$projectPath -e EMAIL=$GITLAB_USER_EMAIL -e BRANCH=$CI_COMMIT_REF_NAME --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest + cache: + paths: + - .gradle + policy: pull + only: + - feature-meta_build + +# 代码检查 sonarqube_analysis: tags: - offline-test - stage: analysis + stage: android-build && analysis image: sonarsource/sonar-scanner-cli:latest dependencies: [] #禁止传递来的artifact script: @@ -27,17 +72,12 @@ sonarqube_analysis: -Dsonar.gitlab.merge_request_discussion=true -Dsonar.java.binaries=. # 如果不使用Maven或Gradle进行分析,则必须手动提供测试二进制文件 only: - - dev - -## 发送简易检测结果报告 -send_sonar_report: - tags: - - offline-test - stage: sendmail - image: hub.shanqu.cc/library/docker:latest - dependencies: [] #禁止传递来的artifact - script: - - group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'` - - docker run -e PROJECTKEY=$group -e EMAIL=$GITLAB_USER_EMAIL --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest - only: - - dev + - feature-meta_build + +## 触发多项目构建 +trigger_job: + stage: deploy-trigger + trigger: + project: devops/automation/build-eci + branch: dev + diff --git a/.gitmodules b/.gitmodules index b5e90c44ba..7cbef87053 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,13 +1,13 @@ [submodule "libraries/LGLibrary"] path = libraries/LGLibrary - url = git@git.shanqu.cc:android/common-library.git + url = ../../../android/common-library.git branch = master [submodule "vspace-bridge"] path = vspace-bridge - url = git@git.shanqu.cc:cwzs/android/vspace-bridge.git + url = ../../../cwzs/android/vspace-bridge.git [submodule "module_common/src/debug/assets/assistant-android-mock"] path = module_common/src/debug/assets/assistant-android-mock - url = git@git.shanqu.cc:halo/android/assistant-android-mock.git + url = ../../../halo/android/assistant-android-mock.git [submodule "ndownload"] path = ndownload - url = git@git.shanqu.cc:android/ndownload.git + url = ../../../android/ndownload.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..ab194bd3e4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM openjdk:11-jdk +WORKDIR /project +SHELL ["/bin/bash", "-c"] +#配置SDK环境变量 +ENV ANDROID_SDK_ROOT /usr/lib/sdk +ENV ANDROID_HOME /usr/lib/sdk +ENV PATH $ANDROID_SDK_ROOT:$PATH +ENV PATH=$PATH:${ANDROID_HOME}/cmdline-tools/cmdline-tools/bin/ +ENV GRADLE_USER_HOME /project/.gradle +RUN source ~/.bashrc +RUN sed -i "s@http://\(deb\|security\).debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list \ + && apt-get --quiet update --yes \ + && apt-get --quiet install --yes lib32stdc++6 lib32z1 libncurses5 util-linux bash tzdata librdkafka-dev pkgconf \ + && rm -rf /var/lib/apt/lists/* +COPY .gradle /project/.gradle diff --git a/app/build.gradle b/app/build.gradle index 04d87bc677..fa225b1a1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,6 +9,9 @@ import groovy.xml.XmlUtil android { + String CONFIG_ID = "" + String FIRST_LAUNCH = "" + buildFeatures { viewBinding true dataBinding true @@ -67,9 +70,13 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt' - /** - * All third-party appid/appkey - */ + // 推广用的配置 id + buildConfigField "String", "CONFIG_ID", "\"${CONFIG_ID}\"" + + // 首次启动的跳转配置 + buildConfigField "String", "FIRST_LAUNCH", "\"${FIRST_LAUNCH}\"" + + // All third-party appid/appkey buildConfigField "String", "API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "VAPI_HOST", "\"${VAPI_HOST}\"" @@ -270,6 +277,7 @@ dependencies { implementation "com.llew.huawei:verifier:${verifier}" teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}" + teaImplementation "com.bytedance.applog:RangersAppLog-All-convert:${bytedanceApplog}" implementation "net.lingala.zip4j:zip4j:${zip4j}" @@ -314,6 +322,7 @@ dependencies { exclude group: 'androidx.swiperefreshlayout' } implementation(project(':module_vpn')) + implementation(project(':module_pkg')) // 默认不接入光能模块,提高编译速度 // debugImplementation(project(':module_energy')) { // exclude group: 'androidx.swiperefreshlayout' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2dba2904cb..30a5d90bdf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,6 +119,8 @@ android:name="io.sentry.breadcrumbs.system-events" android:value="false" /> + + () { - @Override - public void onSuccess(VSetting data) { - mVSetting = data; - SPUtils.setString(Constants.SP_V_SETTINGS, GsonUtils.toJson(data)); - } - }); - } + RetrofitManager.getInstance() + .getVApi().getSettings(BuildConfig.VERSION_NAME) + .subscribeOn(Schedulers.io()) + .subscribe(new BiResponse() { + @Override + public void onSuccess(VSetting data) { + mVSetting = data; + SPUtils.setString(Constants.SP_V_SETTINGS, GsonUtils.toJson(data)); + } + }); RetrofitManager.getInstance() .getApi().getGameGuidePopup(Build.MANUFACTURER, Build.VERSION.RELEASE, Build.MODEL, channel, BuildConfig.VERSION_NAME) diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index d4963820d2..ccd71d880e 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -593,6 +593,7 @@ public class BindingAdapters { case pause: case timeout: case neterror: + case diskisfull: case waiting: progressBar.setText(R.string.downloading); if (downloadEntity.isPluggable() && PackagesManager.isInstalled(downloadEntity.getPackageName())) { diff --git a/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt b/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt index c3405a577d..8ee6a87fa1 100644 --- a/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt +++ b/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt @@ -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) } diff --git a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java index c90244ad23..b43505f4e0 100644 --- a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java +++ b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java @@ -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); diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt index fb705a4000..906a7f3b01 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt @@ -310,6 +310,7 @@ object DownloadItemUtils { DownloadStatus.timeout, DownloadStatus.neterror, DownloadStatus.subscribe, + DownloadStatus.diskisfull, DownloadStatus.overflow -> { buttonStyle = DownloadButton.ButtonStyle.NORMAL setText(R.string.resume) @@ -429,6 +430,7 @@ object DownloadItemUtils { DownloadStatus.pause, DownloadStatus.timeout, DownloadStatus.neterror, + DownloadStatus.diskisfull, DownloadStatus.subscribe, DownloadStatus.overflow -> { if (isMultiVersion) { diff --git a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt index 21231cac0b..33a2f7b659 100644 --- a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt +++ b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt @@ -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") } diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index 6da5470452..cd6d7efa4f 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -87,7 +87,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) ) { @@ -98,7 +100,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 { diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt index cf4a7a4c70..a918f23511 100644 --- a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt +++ b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt @@ -2,16 +2,17 @@ package com.gh.common.xapk import android.annotation.SuppressLint 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 @@ -82,7 +83,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 { @@ -112,7 +113,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) } } @@ -123,7 +124,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) } @@ -157,7 +158,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) } diff --git a/app/src/main/java/com/gh/download/DownloadDataHelper.kt b/app/src/main/java/com/gh/download/DownloadDataHelper.kt index f59615c795..e3d27fbc8a 100644 --- a/app/src/main/java/com/gh/download/DownloadDataHelper.kt +++ b/app/src/main/java/com/gh/download/DownloadDataHelper.kt @@ -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() diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index 2f2be8d42f..0404c8c7ff 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -50,17 +50,17 @@ import com.gh.gamecenter.manager.PackagesManager; import com.gh.gamecenter.login.user.UserManager; import com.gh.gamecenter.packagehelper.PackageRepository; import com.gh.vspace.VHelper; +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; @@ -92,9 +92,9 @@ public class DownloadManager implements DownloadStatusListener { private final Map> gameMap; private final ArrayMap statusMap; - private final ArrayMap downloadingMap; + private final ConcurrentHashMap downloadingMap; - private ArrayList mInvisiblePendingTaskList; // 用户不可见的 pending 任务 + private final ArrayList mInvisiblePendingTaskList; // 用户不可见的 pending 任务 private final DownloadDao mDownloadDao; private final DownloadedGameIdAndPackageNameDao mDownloadedGameIdAndPackageNameDao; @@ -170,8 +170,6 @@ public class DownloadManager implements DownloadStatusListener { mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK); - DownloadStatusManager.getInstance().registerTaskStatusListener(this); - // 只有下载模块需要这坨东西,因此移动到这里初始化 ConnectionUtils.initHttpsUrlConnection(mContext); @@ -181,7 +179,7 @@ public class DownloadManager implements DownloadStatusListener { platformMap = new ArrayMap<>(); gameMap = new ConcurrentHashMap<>(); statusMap = new ArrayMap<>(); - downloadingMap = new ArrayMap<>(); + downloadingMap = new ConcurrentHashMap<>(); // mDownloadSnapshotList = new ArrayList<>(); mInvisiblePendingTaskList = new ArrayList<>(); @@ -227,10 +225,6 @@ public class DownloadManager implements DownloadStatusListener { } } - public ArrayMap getDownloadingMap() { - return downloadingMap; - } - public static DownloadManager getInstance() { return SingletonHolder.INSTANCE; } @@ -446,7 +440,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); } @@ -467,7 +461,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) { @@ -502,7 +496,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); } @@ -548,7 +542,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; } @@ -556,7 +550,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; @@ -585,6 +579,16 @@ public class DownloadManager implements DownloadStatusListener { return mDownloadDao.getAllSnapshots(); } + /** + * 获取快照 + * + * @param url 下载地址 + */ + @Nullable + public DownloadEntity getDownloadEntitySnapshot(String url) { + return mDownloadDao.getSnapshot(url); + } + /** * 获取快照 * @@ -825,6 +829,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) { @@ -858,19 +864,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"); @@ -880,7 +883,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"); @@ -891,7 +894,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()); @@ -907,14 +910,14 @@ public class DownloadManager implements DownloadStatusListener { * 3.检查是否显示下载通知栏 */ public void initDownloadService() { - final List urlList = new ArrayList<>(DataChanger.INSTANCE.getDownloadingTasks().keySet()); + final List 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); } } @@ -927,7 +930,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); } @@ -937,7 +940,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); } /** @@ -953,11 +956,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 { /* @@ -997,7 +1000,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); @@ -1128,7 +1131,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; } } @@ -1168,12 +1171,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; } } @@ -1231,7 +1234,7 @@ public class DownloadManager implements DownloadStatusListener { * 更新数据库中的下载实体 */ public void updateDownloadEntity(DownloadEntity downloadEntity) { - mDownloadDao.newOrUpdate(downloadEntity); + mDownloadDao.update(downloadEntity, false); } /** diff --git a/app/src/main/java/com/gh/download/simple/DownloadMessageHandler.kt b/app/src/main/java/com/gh/download/simple/DownloadMessageHandler.kt index c4ba87f885..5e37bfb2e9 100644 --- a/app/src/main/java/com/gh/download/simple/DownloadMessageHandler.kt +++ b/app/src/main/java/com/gh/download/simple/DownloadMessageHandler.kt @@ -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) { diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index 834d35f6ed..2e048734d5 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -37,6 +37,7 @@ import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.entity.PrivacyPolicyEntity import com.gh.gamecenter.feature.utils.PlatformUtils +import com.gh.gamecenter.pkg.PkgHelper import com.gh.vspace.VHelper import com.halo.assistant.HaloApp import com.lightgame.download.FileUtils @@ -386,6 +387,10 @@ class SplashScreenActivity : BaseActivity() { checkAndPostUsageStats() updateGameSubstituteRepository() + if (BuildConfig.CONFIG_ID.isNotEmpty()) { + PkgHelper.requestPkgConfig(BuildConfig.CONFIG_ID) + } + // 获取自动刷新的cd,获取版本对应表 val time = mSharedPreferences!!.getString("refresh_time", null) val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) diff --git a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java index ea9b774f00..be0f28e376 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java @@ -162,7 +162,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 { diff --git a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java index 3d7cfdbaf7..6296cf8297 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java @@ -238,6 +238,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { } 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); @@ -247,6 +248,8 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { || 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("已暂停"); } diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt index d1204aba72..dc31f26f03 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt @@ -11,6 +11,7 @@ import com.gh.gamecenter.common.utils.singleToMain import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.entity.HomeDataEntity import com.gh.gamecenter.entity.SubjectRecommendEntity +import com.gh.gamecenter.pkg.PkgHelper import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp @@ -64,6 +65,20 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode if (homeTab.size == 0) { homeTab.add(SubjectRecommendEntity(type = "home")) } + + // 从推广包配置信息里找是否需要默认选中一个 tab + PkgHelper.getPkgConfig()?.let { pkgLinkEntity -> + if (pkgLinkEntity.isHomeTopTabLink) { + PkgHelper.markConfigUsed() + for ((index, tab) in homeTab.withIndex()) { + if (pkgLinkEntity.type == tab.type && pkgLinkEntity.link == tab.link) { + defaultTabPosition = index + break + } + } + } + } + if (!isRefresh) { tabs.postValue(homeTab) } diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java index c1d2122b2d..720e23ddd9 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java @@ -29,6 +29,7 @@ import com.facebook.imagepipeline.image.ImageInfo; import com.gh.common.constant.Config; import com.gh.common.dialog.PrivacyPolicyDialogFragment; import com.gh.common.dialog.ReserveDialog; +import com.gh.common.util.DirectUtils; import com.gh.common.util.HomeBottomBarHelper; import com.gh.common.util.IntegralLogHelper; import com.gh.common.util.LogUtils; @@ -41,6 +42,7 @@ import com.gh.gamecenter.common.callback.BiCallback; import com.gh.gamecenter.common.callback.OnDoubleTapListener; import com.gh.gamecenter.common.constant.Constants; import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.entity.PkgConfigEntity; import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.syncpage.SyncPageRepository; import com.gh.gamecenter.common.tracker.TrackerLogger; @@ -67,6 +69,7 @@ import com.gh.gamecenter.login.entity.UserInfoEntity; import com.gh.gamecenter.message.MessageUnreadRepository; import com.gh.gamecenter.message.MessageUnreadViewModel; import com.gh.gamecenter.personal.HaloPersonalFragment; +import com.gh.gamecenter.pkg.PkgHelper; import com.gh.gamecenter.servers.GameServersPublishFragment; import com.gh.gamecenter.servers.GameServersTestFragment; import com.gh.gamecenter.subject.SubjectFragment; @@ -266,11 +269,47 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem } }); + applyPkgConfig(); + ViewModelProviders.of(this) .get(MessageUnreadViewModel.class) .getUnreadMessageTotalLiveData().observe(this, isShow -> ExtensionsKt.goneIf(mBinding.mainIvMessageHint, !isShow)); } + private void applyPkgConfig() { + PkgConfigEntity.PkgLinkEntity pkgLinkEntity = PkgHelper.INSTANCE.getPkgConfig(); + if (pkgLinkEntity != null && !pkgLinkEntity.isHomeTopTabLink()) { + String bottomTab = pkgLinkEntity.getHomeBottomTab(); + + PkgHelper.INSTANCE.markConfigUsed(); + if (!TextUtils.isEmpty(bottomTab)) { + // TODO 根据具体 tab 来作为跳转的具体位置,避免硬编码 + int targetIndex = INDEX_HOME; + + switch (bottomTab) { + case "game_lib": + targetIndex = INDEX_GAME; + break; + case "community": + targetIndex = INDEX_BBS; + break; + case "video": + targetIndex = INDEX_VIDEO; + break; + case "gh": + targetIndex = INDEX_PERSONAL; + break; + } + + mViewPager.setCurrentItem(targetIndex); + onPageChanged(targetIndex); + changeColor(targetIndex); + } else { + DirectUtils.directToLinkPage(requireContext(), pkgLinkEntity, "推广包配置", "首页"); + } + } + } + public void getDialog() { mViewModel.requestOpeningData(); mViewModel.getPrivacyPolicyDialog().observe(this, it -> { diff --git a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java index 97c3d812ab..f2d2b5c6f6 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java @@ -47,8 +47,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 +158,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())) { @@ -479,7 +481,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, "不再是静默更新"); diff --git a/app/src/main/java/com/gh/gamecenter/pkg/PkgHelper.kt b/app/src/main/java/com/gh/gamecenter/pkg/PkgHelper.kt new file mode 100644 index 0000000000..2d0da1e62a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/pkg/PkgHelper.kt @@ -0,0 +1,47 @@ +package com.gh.gamecenter.pkg + +import android.util.Base64 +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.entity.PkgConfigEntity +import com.gh.gamecenter.common.utils.toObject +import com.gh.gamecenter.core.provider.IPkgProvider +import com.gh.gamecenter.core.utils.SPUtils + +object PkgHelper { + + private var mPkgConfigLink: PkgConfigEntity.PkgLinkEntity? = null + private const val SP_PKG_CONFIG_IS_USED = "pkg_config_is_used" + + private val mPkgProvider by lazy { + ARouter.getInstance().build(RouteConsts.provider.pkg).navigation() as? IPkgProvider + } + + fun getPkgConfig(): PkgConfigEntity.PkgLinkEntity? { + if (mPkgConfigLink == null + && !SPUtils.getBoolean(SP_PKG_CONFIG_IS_USED, false) + && BuildConfig.FIRST_LAUNCH.isNotEmpty()) { + mPkgConfigLink = Base64.decode(BuildConfig.FIRST_LAUNCH, Base64.DEFAULT).toString().toObject() + } + + return mPkgConfigLink + } + + fun markConfigUsed() { + mPkgConfigLink = null + SPUtils.setBoolean(SP_PKG_CONFIG_IS_USED, true) + } + + /** + * 获取特殊包配置 + */ + fun requestPkgConfig(configId: String) { + if (SPUtils.getBoolean(SP_PKG_CONFIG_IS_USED, false)) return + + mPkgProvider?.requestPkgConfig(configId) { + mPkgConfigLink = it.data?.link + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/ndownload/NDataChanger.kt b/app/src/main/java/com/gh/ndownload/NDataChanger.kt new file mode 100644 index 0000000000..434e8c4847 --- /dev/null +++ b/app/src/main/java/com/gh/ndownload/NDataChanger.kt @@ -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 = Collections.synchronizedMap(HashMap())//当前正在下载的任务队列 + val downloadEntries: MutableMap = 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() + } + } + +} diff --git a/app/src/main/java/com/gh/ndownload/NDownloadBridge.kt b/app/src/main/java/com/gh/ndownload/NDownloadBridge.kt new file mode 100644 index 0000000000..0b37db83ba --- /dev/null +++ b/app/src/main/java/com/gh/ndownload/NDownloadBridge.kt @@ -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 by lazy { ConcurrentHashMap() } + + private val mStartUpTimeMap: ConcurrentHashMap 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() ?: 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() ?: 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?) + .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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/ndownload/NDownloadService.java b/app/src/main/java/com/gh/ndownload/NDownloadService.java new file mode 100644 index 0000000000..c894bafdcf --- /dev/null +++ b/app/src/main/java/com/gh/ndownload/NDownloadService.java @@ -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(); + } + } + +} diff --git a/app/src/main/java/com/gh/ndownload/NHttpClient.kt b/app/src/main/java/com/gh/ndownload/NHttpClient.kt new file mode 100644 index 0000000000..b78593684d --- /dev/null +++ b/app/src/main/java/com/gh/ndownload/NHttpClient.kt @@ -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?) { + 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) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt b/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt index ce112c3b92..a5e4895682 100644 --- a/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt +++ b/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt @@ -188,6 +188,7 @@ class HomeRecentVGameAdapter(context: Context) : DiffUtilAdapter( DownloadStatus.uncertificated, DownloadStatus.unqualified, DownloadStatus.notfound, + DownloadStatus.diskisfull, DownloadStatus.unavailable, DownloadStatus.overflow -> { binding.root.setOnClickListener { diff --git a/app/src/main/java/com/gh/vspace/VArchiveHelper.kt b/app/src/main/java/com/gh/vspace/VArchiveHelper.kt index b59303f2f8..d15a0cb6bb 100644 --- a/app/src/main/java/com/gh/vspace/VArchiveHelper.kt +++ b/app/src/main/java/com/gh/vspace/VArchiveHelper.kt @@ -100,7 +100,7 @@ object VArchiveHelper { .setHttpClient(DefaultHttpClient()) .setDownloadThreadSize(2) .setDownloadListener(DownloadMessageHandler) - .setDownloadExecutor(AppExecutor.ioExecutor) + .setDownloadExecutor(AppExecutor.downloadExecutor) .build() ) } diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt index 3b4640a62c..2329399b52 100644 --- a/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt @@ -499,6 +499,7 @@ class VDownloadManagerAdapter( DownloadStatus.timeout, DownloadStatus.neterror, DownloadStatus.subscribe, + DownloadStatus.diskisfull, DownloadStatus.overflow -> { btnText = context.getString(R.string.resume) downloadDes.visibility = View.VISIBLE diff --git a/app/src/main/java/com/gh/vspace/VGameItemData.kt b/app/src/main/java/com/gh/vspace/VGameItemData.kt index 2b97be3b77..3f74d40e92 100644 --- a/app/src/main/java/com/gh/vspace/VGameItemData.kt +++ b/app/src/main/java/com/gh/vspace/VGameItemData.kt @@ -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 diff --git a/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt b/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt index 67ba0bb576..f221204b96 100644 --- a/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt +++ b/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt @@ -183,6 +183,7 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() { overflow, timeout, neterror, + diskisfull, waiting, subscribe -> { downloadBtn.setText(R.string.waiting) diff --git a/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt b/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt index 374630c8b4..7a027c2c16 100644 --- a/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt +++ b/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt @@ -48,6 +48,7 @@ class VSpaceLoadingFragment : BaseFragment() { hijack, notfound, neterror, + diskisfull, overflow -> requireActivity().finish() unqualified -> { ToastUtils.toast("暂不支持未成年人下载") diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index eac1d7100e..e931f2532b 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -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()); diff --git a/app/src/main/res/layout/activity_editor_insert_game.xml b/app/src/main/res/layout/activity_editor_insert_game.xml index ece4c9b7f8..013cf9a008 100644 --- a/app/src/main/res/layout/activity_editor_insert_game.xml +++ b/app/src/main/res/layout/activity_editor_insert_game.xml @@ -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"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> + app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"> "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 Data–2G 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 Data–3G 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 Data–4G - TelephonyManager.NETWORK_TYPE_LTE -> "Cellular - 4G" - else -> "Cellular - Unknown Generation" + TelephonyManager.NETWORK_TYPE_LTE -> "4G" + TelephonyManager.NETWORK_TYPE_NR -> "5G" + else -> "unknown" } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/DeviceUtils.java b/module_common/src/main/java/com/gh/gamecenter/common/utils/DeviceUtils.java index ae6b67ff85..7cbd5b1295 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/DeviceUtils.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/DeviceUtils.java @@ -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; diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt index 53aa9e6af2..2109cd9632 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt @@ -69,6 +69,7 @@ import okhttp3.RequestBody import org.json.JSONArray import org.json.JSONObject import java.lang.reflect.ParameterizedType +import java.io.* import java.net.URI import java.util.* import java.util.concurrent.TimeUnit @@ -413,6 +414,25 @@ fun Context.showRegulationTestDialogIfNeeded(action: (() -> Unit)) { } } +/** + * Serializable 相关 + */ +fun 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 */ diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/PermissionHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/PermissionHelper.kt index cab57313b4..53ffe7bc0e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/PermissionHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/PermissionHelper.kt @@ -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 diff --git a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt index 64bbd95b9b..f3173d99b4 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt @@ -60,6 +60,10 @@ object AppExecutor { ) } + val downloadExecutor: ExecutorService by lazy { + Executors.newCachedThreadPool(GHThreadFactory("GH_DOWNLOAD_THREAD")) + } + val cachedScheduler by lazy { Schedulers.from(ioExecutor) } class MainThreadExecutor : Executor { diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IPkgProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IPkgProvider.kt new file mode 100644 index 0000000000..e296ee8fb9 --- /dev/null +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IPkgProvider.kt @@ -0,0 +1,9 @@ +package com.gh.gamecenter.core.provider + +import com.alibaba.android.arouter.facade.template.IProvider + +interface IPkgProvider : IProvider { + + fun requestPkgConfig(configId: String, onConfigReceived: (config: T) -> Unit) + +} \ No newline at end of file diff --git a/module_pkg/.gitignore b/module_pkg/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/module_pkg/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/module_pkg/build.gradle b/module_pkg/build.gradle new file mode 100644 index 0000000000..335b198c04 --- /dev/null +++ b/module_pkg/build.gradle @@ -0,0 +1,87 @@ +if (isRelease.toBoolean()) { + apply plugin: 'com.android.library' +} else { + apply plugin: 'com.android.application' +} +apply plugin: 'org.jetbrains.kotlin.android' +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-parcelize' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + if (!isRelease.toBoolean()) { + applicationId "com.gh.pkg" + multiDexEnabled true + } + minSdk rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + + buildConfigField "String", "API_HOST", "\"${API_HOST}\"" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + sourceSets { + main { + if (isRelease.toBoolean()) { + manifest.srcFile 'src/main/AndroidManifest.xml' + java { + exclude 'manifest/**' + } + } else { + java { + srcDirs = ['src/main/java', "src/pkg/java"] + } + manifest.srcFile 'src/main/manifest/AndroidManifest.xml' + res.srcDirs = ['src/main/res','src/pkg/res'] + } + } + } + + kapt { + arguments { + arg("AROUTER_MODULE_NAME", project.name) + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + buildTypes { + debug { + if (!isRelease.toBoolean()) { + buildConfigField "String", "DEV_API_HOST", "\"${DEV_API_HOST}\"" + } + } + + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + + if (!isRelease.toBoolean()) { + buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" + } + } + } + +} + +dependencies { + implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') + kapt "com.alibaba:arouter-compiler:$arouterVersion" + if (!isRelease.toBoolean()) { + implementation "androidx.multidex:multidex:${multiDex}" + } + + compileOnly(project(path: ":module_common")) +} \ No newline at end of file diff --git a/module_pkg/proguard-rules.pro b/module_pkg/proguard-rules.pro new file mode 100644 index 0000000000..817f2247a0 --- /dev/null +++ b/module_pkg/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-renamesourcefileattribute SourceFile +# Keep Attribute +-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable \ No newline at end of file diff --git a/module_pkg/src/main/AndroidManifest.xml b/module_pkg/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ec6c511cd2 --- /dev/null +++ b/module_pkg/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/module_pkg/src/main/java/com/gh/gamecenter/pkg/HaloApp.kt b/module_pkg/src/main/java/com/gh/gamecenter/pkg/HaloApp.kt new file mode 100644 index 0000000000..d53e9c0c2a --- /dev/null +++ b/module_pkg/src/main/java/com/gh/gamecenter/pkg/HaloApp.kt @@ -0,0 +1,44 @@ +package com.gh.gamecenter.pkg + +import android.app.Application +import android.content.res.Configuration +import com.gh.gamecenter.core.iinterface.IApplication +import com.google.auto.service.AutoService + +@AutoService(IApplication::class) +class HaloApp : IApplication { + + override fun attachBaseContext() { + // Do nothing + } + + override fun onCreate(application: Application) { + mApp = application + } + + + override fun onLowMemory() { + // Do nothing + } + + override fun onTerminate() { + // Do nothing + } + + override fun onTrimMemory(level: Int) { + // Do nothing + } + + override fun onConfigurationChanged(newConfig: Configuration) { + // Do nothing + } + + companion object { + private lateinit var mApp: Application + + @JvmStatic + fun getInstance(): Application { + return mApp + } + } +} diff --git a/module_pkg/src/main/java/com/gh/gamecenter/pkg/PkgProviderImpl.kt b/module_pkg/src/main/java/com/gh/gamecenter/pkg/PkgProviderImpl.kt new file mode 100644 index 0000000000..f120890b16 --- /dev/null +++ b/module_pkg/src/main/java/com/gh/gamecenter/pkg/PkgProviderImpl.kt @@ -0,0 +1,32 @@ +package com.gh.gamecenter.pkg + +import android.annotation.SuppressLint +import android.content.Context +import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.entity.PkgConfigEntity +import com.gh.gamecenter.core.provider.IPkgProvider +import com.gh.gamecenter.pkg.retrofit.RetrofitManager +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers + +@Route(path = RouteConsts.provider.pkg, name = "PKG 暴露服务") +class PkgProviderImpl : IPkgProvider { + + @SuppressLint("CheckResult") + override fun requestPkgConfig(configId: String, onConfigReceived: (config: PkgConfigEntity) -> Unit) { + RetrofitManager.getInstance().api.getPkgConfig(configId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + onConfigReceived.invoke(it) + }, { + it.printStackTrace() + }) + } + + override fun init(context: Context?) { + // Do nothing + } + +} \ No newline at end of file diff --git a/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/ApiService.kt b/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/ApiService.kt new file mode 100644 index 0000000000..97d426ccb1 --- /dev/null +++ b/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/ApiService.kt @@ -0,0 +1,15 @@ +package com.gh.gamecenter.pkg.retrofit + +import com.gh.gamecenter.common.entity.PkgConfigEntity +import io.reactivex.Single +import retrofit2.http.* + +interface ApiService { + + /** + * 获取推广包配置 + */ + @GET("pkg/{path}") + fun getPkgConfig(@Path("path") config: String): Single + +} \ No newline at end of file diff --git a/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/RetrofitManager.kt b/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/RetrofitManager.kt new file mode 100644 index 0000000000..b9c11b65cf --- /dev/null +++ b/module_pkg/src/main/java/com/gh/gamecenter/pkg/retrofit/RetrofitManager.kt @@ -0,0 +1,20 @@ +package com.gh.gamecenter.pkg.retrofit + +import com.gh.gamecenter.common.retrofit.BaseRetrofitManager +import com.gh.gamecenter.common.utils.EnvHelper.getHost +import com.gh.gamecenter.core.utils.SingletonHolder +import com.gh.gamecenter.pkg.HaloApp + +class RetrofitManager private constructor() : BaseRetrofitManager() { + + val api: ApiService + + init { + val context = HaloApp.getInstance().applicationContext + val okHttpNormalConfig = getOkHttpConfig(context, 0, 2) + api = provideService(okHttpNormalConfig, getHost(), ApiService::class.java) + } + + companion object : SingletonHolder(::RetrofitManager) + +} \ No newline at end of file diff --git a/module_pkg/src/main/manifest/AndroidManifest.xml b/module_pkg/src/main/manifest/AndroidManifest.xml new file mode 100644 index 0000000000..7ad06ce800 --- /dev/null +++ b/module_pkg/src/main/manifest/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/module_pkg/src/main/res/values/strings.xml b/module_pkg/src/main/res/values/strings.xml new file mode 100644 index 0000000000..b55f29d59c --- /dev/null +++ b/module_pkg/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + PKG + diff --git a/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/PkgModuleApp.kt b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/PkgModuleApp.kt new file mode 100644 index 0000000000..cf9c45b56a --- /dev/null +++ b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/PkgModuleApp.kt @@ -0,0 +1,36 @@ +package com.gh.gamecenter.pkg + +import androidx.multidex.MultiDexApplication +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.gamecenter.core.iinterface.IApplication +import java.util.* + +class PkgModuleApp : MultiDexApplication() { + + private val mApplicationList = ServiceLoader.load(IApplication::class.java, this.javaClass.classLoader) + + override fun onCreate() { + super.onCreate() + initArouter() + mApp = this + for (application in mApplicationList) { + application.onCreate(this) + } + } + + private fun initArouter() { + if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 + ARouter.openLog() // 打印日志 + ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) + } + ARouter.init(this) // 尽可能早,推荐在Application中初始化 + } + + companion object { + private lateinit var mApp: PkgModuleApp + + fun getInstance(): PkgModuleApp { + return mApp + } + } +} \ No newline at end of file diff --git a/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/provider/AppProviderImpl.kt b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/provider/AppProviderImpl.kt new file mode 100644 index 0000000000..36d617cca9 --- /dev/null +++ b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/provider/AppProviderImpl.kt @@ -0,0 +1,86 @@ +package com.gh.gamecenter.pkg.provider + +import android.app.Activity +import android.app.Application +import android.content.Context +import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.core.provider.IAppProvider +import com.gh.gamecenter.core.provider.IFlavorProvider +import com.gh.gamecenter.pkg.HaloApp +import com.gh.gamecenter.pkg.R + +@Route(path = RouteConsts.provider.app, name = "Application暴露服务") +class AppProviderImpl : IAppProvider { + override fun init(context: Context?) { + // Do nothing + } + + override fun get(key: String, isRemove: Boolean): Any? { + return "" + } + + override fun put(key: String, any: Any) { + // do nothing + } + + override fun getAppName(): String { + return HaloApp.getInstance().getString(R.string.app_name) + } + + override fun getGid(): String { + return "" + } + + override fun refreshGid() { + // Do nothing + } + + override fun getOaid(): String { + return "" + } + + override fun getChannel(): String { + return "" + } + + override fun setChannel(channel: String) { + // Do nothing + } + + override fun getUserAgent(): String { + return "" + } + + override fun getServerUserMark(): String { + return "" + } + + override fun getDeviceRamSize(): Long { + return 0L + } + + override fun getTemporaryLocalDeviceId(): String { + return "" + } + + override fun getFlavorProvider(): IFlavorProvider { + return object : IFlavorProvider { + override fun getChannelStr(application: Application): String { + return "" + } + + override fun init(application: Application, activity: Activity) { + // do nothing + } + + override fun logEvent(content: String) { + // do nothing + } + } + } + + override fun isUserAcceptPrivacyPolicy(context: Context): Boolean { + return true + } +} \ No newline at end of file diff --git a/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/view/PkgActivity.kt b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/view/PkgActivity.kt new file mode 100644 index 0000000000..957baa097f --- /dev/null +++ b/module_pkg/src/pkg/java/com/gh/gamecenter/pkg/view/PkgActivity.kt @@ -0,0 +1,15 @@ +package com.gh.gamecenter.pkg.view + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.gh.gamecenter.pkg.R + +class PkgActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_pkg) + } + +} \ No newline at end of file diff --git a/module_pkg/src/pkg/res/layout/activity_pkg.xml b/module_pkg/src/pkg/res/layout/activity_pkg.xml new file mode 100644 index 0000000000..95b55a35a0 --- /dev/null +++ b/module_pkg/src/pkg/res/layout/activity_pkg.xml @@ -0,0 +1,15 @@ + + + +