diff --git a/app/src/main/java/com/gh/base/BaseSimpleDao.kt b/app/src/main/java/com/gh/base/BaseSimpleDao.kt new file mode 100644 index 0000000000..147c777753 --- /dev/null +++ b/app/src/main/java/com/gh/base/BaseSimpleDao.kt @@ -0,0 +1,93 @@ +package com.gh.base + +import android.content.Context +import android.content.SharedPreferences +import com.gh.common.util.SPUtils +import com.halo.assistant.HaloApp + +/** + * 用 SP 实现的简单列表持久化结构 + */ +abstract class BaseSimpleDao { + + // 使用独有的 SP 文件 + private val mSp: SharedPreferences by lazy { + HaloApp.getInstance().application.getSharedPreferences("SimpleDao", Context.MODE_PRIVATE) + } + + fun add(key: String) { + val originString = SPUtils.getString(mSp, getSPKey()) + + if (originString.isEmpty()) { + // Insert keyword only for the very first time. + SPUtils.setString(mSp, getSPKey(), key) + } else { + getAll()?.let { + if (getMaxSize() != -1 && it.size > getMaxSize()) { + it.removeAt(it.size - 1) + } + + // Move keyword to the very front if it exists. + if (it.contains(key)) { + it.remove(key) + } + it.add(0, key) + val builder = StringBuilder() + for ((index, k) in it.withIndex()) { + builder.append(k) + if (index != it.size - 1) { + builder.append(DIVIDER_KEY) + } + } + SPUtils.setString(mSp, getSPKey(), builder.toString()) + } + } + } + + fun delete(key: String) { + val originString = SPUtils.getString(mSp, getSPKey()) + + if (originString.isEmpty()) { + // do nothing + } else { + getAll()?.let { + if (it.contains(key)) { + it.remove(key) + } + val builder = StringBuilder() + for ((index, k) in it.withIndex()) { + builder.append(k) + if (index != it.size - 1) { + builder.append(DIVIDER_KEY) + } + } + SPUtils.setString(mSp, getSPKey(), builder.toString()) + } + } + } + + fun getAll(): ArrayList? { + val list = SPUtils.getString(mSp, getSPKey()).split(DIVIDER_KEY) + + return if (list.size == 1 && list[0].isEmpty()) null else ArrayList(list) + } + + fun getRawString(): String = SPUtils.getString(mSp, getSPKey()) + + fun contains(key: String): Boolean { + return getAll()?.contains(key) == true + } + + fun deleteAll() { + SPUtils.setString(mSp, getSPKey(), "") + } + + open fun getMaxSize(): Int = -1 + + abstract fun getSPKey(): String + + companion object { + private const val DIVIDER_KEY = "<-||->" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/DefaultJsApi.kt b/app/src/main/java/com/gh/common/DefaultJsApi.kt index 4d3c2fcef3..3587e0a99c 100644 --- a/app/src/main/java/com/gh/common/DefaultJsApi.kt +++ b/app/src/main/java/com/gh/common/DefaultJsApi.kt @@ -108,7 +108,7 @@ class DefaultJsApi(var context: Context) { @JavascriptInterface fun getAppVersionCode(msg: Any): Int { - return PackageUtils.getVersionCode() + return PackageUtils.getGhVersionCode() } @JavascriptInterface diff --git a/app/src/main/java/com/gh/common/constant/Config.java b/app/src/main/java/com/gh/common/constant/Config.java index 39b990b3f5..174b4b684f 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -280,7 +280,7 @@ public class Config { public static void getGhzsSettings() { String channel = HaloApp.getInstance().getChannel(); RetrofitManager.getInstance(HaloApp.getInstance().getApplication()) - .getSensitiveApi().getSettings(PackageUtils.getVersionName(), channel) + .getSensitiveApi().getSettings(PackageUtils.getGhVersionName(), channel) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Response() { diff --git a/app/src/main/java/com/gh/common/constant/Constants.java b/app/src/main/java/com/gh/common/constant/Constants.java index fe82cf3855..7665fde049 100644 --- a/app/src/main/java/com/gh/common/constant/Constants.java +++ b/app/src/main/java/com/gh/common/constant/Constants.java @@ -93,11 +93,11 @@ public class Constants { // 今天是否已经触发了 “通知管理” 引导弹窗 public static final String SP_IS_SHOWED_NOTIFICATION_TODAY = "show_is_notification_today"; // v4.0.0已废弃,标记安装的游戏为已玩过弹窗,最多取消2次 (https://gitlab.ghzs.com/pm/halo-app-issues/issues/722 调整为版本相关) (不是常量了也放这里好像有点奇怪) - public static final String SP_MARK_INSTALLED_GAME = "mark_installed_game" + PackageUtils.getVersionName(); + public static final String SP_MARK_INSTALLED_GAME = "mark_installed_game" + PackageUtils.getGhVersionName(); // 标记安装的游戏为已玩过弹窗(个人主页最多弹一次) - public static final String SP_MARK_INSTALLED_GAME_USER_HOME = "mark_installed_game_user_home" + PackageUtils.getVersionName(); + public static final String SP_MARK_INSTALLED_GAME_USER_HOME = "mark_installed_game_user_home" + PackageUtils.getGhVersionName(); // 标记安装的游戏为已玩过弹窗(我的游戏最多弹一次) - public static final String SP_MARK_INSTALLED_GAME_MY_GAME = "mark_installed_game_my_game" + PackageUtils.getVersionName(); + public static final String SP_MARK_INSTALLED_GAME_MY_GAME = "mark_installed_game_my_game" + PackageUtils.getGhVersionName(); //视频详情滑动引导 public static final String SP_SHOW_SLIDE_GUIDE = "show_slide_guide"; //视频详情点击引导 diff --git a/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt b/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt index 37042e04f6..c147810094 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt @@ -76,7 +76,7 @@ object ExposureUtils { if (PackageUtils.isCanUpdate(apkEntity, gameId)) { DownloadType.PLUGIN_UPDATE } else { - if (Version(apkEntity.version).isHigherThan(PackageUtils.getVersionByPackage(apkEntity.packageName))) { + if (Version(apkEntity.version).isHigherThan(PackageUtils.getVersionNameByPackageName(apkEntity.packageName))) { DownloadType.UPDATE } else { DownloadType.DOWNLOAD 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 fd02bf3931..778826c6e9 100644 --- a/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt +++ b/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt @@ -112,7 +112,7 @@ class SimulatorDownloadManager private constructor() { return } val isInstalled = PackageUtils.isInstalledFromAllPackage(context, simulator?.apk?.packageName) - val versionFromInstalledApp = PackageUtils.getVersionByPackage(simulator?.apk?.packageName) + val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator?.apk?.packageName) val shouldShowUpdate = Version(simulator?.apk?.version).isHigherThan(versionFromInstalledApp) val title = if (shouldShowUpdate && isInstalled) "更新模拟器" else "安装模拟器" val message = if (shouldShowUpdate && isInstalled) "检测到模拟器存在更高版本,是否前往更新" else "模拟器游戏需要先下载安装对应的模拟器,才可以运行" diff --git a/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt b/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt index ff93f116bb..0bd81702a8 100644 --- a/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt +++ b/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt @@ -85,7 +85,7 @@ object SimulatorGameManager { @JvmStatic fun launchSimulatorGame(downloadEntity: DownloadEntity, gameEntity: GameEntity) { - val versionFromInstalledApp = PackageUtils.getVersionByPackage(gameEntity.simulator?.apk?.packageName) + val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(gameEntity.simulator?.apk?.packageName) val shouldShowUpdate = Version(gameEntity.simulator?.apk?.version).isHigherThan(versionFromInstalledApp) updateSimulatorConfigFile(gameId = gameEntity.id) diff --git a/app/src/main/java/com/gh/common/util/DataLogUtils.java b/app/src/main/java/com/gh/common/util/DataLogUtils.java index df7f2de0fc..21440726c0 100644 --- a/app/src/main/java/com/gh/common/util/DataLogUtils.java +++ b/app/src/main/java/com/gh/common/util/DataLogUtils.java @@ -48,7 +48,7 @@ public class DataLogUtils { // 上传日志 public static void uploadLog(Context context, String topic, Map map) { - String version = PackageUtils.getVersionName(); + String version = PackageUtils.getGhVersionName(); String user = Installation.getUUID(context); String channel = HaloApp.getInstance().getChannel(); map.put("version", version); diff --git a/app/src/main/java/com/gh/common/util/DialogHelper.kt b/app/src/main/java/com/gh/common/util/DialogHelper.kt index 68d5158cb6..fba1d088e3 100644 --- a/app/src/main/java/com/gh/common/util/DialogHelper.kt +++ b/app/src/main/java/com/gh/common/util/DialogHelper.kt @@ -65,6 +65,7 @@ object DialogHelper { if (it.showCloseIcon) { binding.closeContainer.visibility = View.VISIBLE + binding.closeContainer.enlargeTouchArea() binding.closeContainer.setOnClickListener { dialog.dismiss() } } } diff --git a/app/src/main/java/com/gh/common/util/DownloadDialogHelper.kt b/app/src/main/java/com/gh/common/util/DownloadDialogHelper.kt index 7e5814a9e3..314ebe9f38 100644 --- a/app/src/main/java/com/gh/common/util/DownloadDialogHelper.kt +++ b/app/src/main/java/com/gh/common/util/DownloadDialogHelper.kt @@ -29,7 +29,7 @@ object DownloadDialogHelper { if (downloadDialog.isNullOrEmpty()) return null for (dialog in downloadDialog) { - val versionName = PackageUtils.getVersionName() + val versionName = PackageUtils.getGhVersionName() val noticeVersions = dialog.rule.noticeVersions if (noticeVersions.isEmpty() || noticeVersions.contains(versionName)) { // 共有 8 种可能 diff --git a/app/src/main/java/com/gh/common/util/Extensions.kt b/app/src/main/java/com/gh/common/util/Extensions.kt index 5e60700ce9..c67a9c3d2a 100644 --- a/app/src/main/java/com/gh/common/util/Extensions.kt +++ b/app/src/main/java/com/gh/common/util/Extensions.kt @@ -12,6 +12,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Build import android.text.* +import android.text.format.Formatter import android.text.style.ClickableSpan import android.text.style.ImageSpan import android.text.style.URLSpan @@ -852,6 +853,13 @@ fun Int.toSimpleCount(): String { return NumberUtils.transSimpleCount(this) } +/** + * Long related + */ +fun Long.toProperReadableSize(): String { + return Formatter.formatFileSize(HaloApp.getInstance().application, this) +} + /** * Image related */ diff --git a/app/src/main/java/com/gh/common/util/GameUtils.java b/app/src/main/java/com/gh/common/util/GameUtils.java index 90e368995e..0b55a93c57 100644 --- a/app/src/main/java/com/gh/common/util/GameUtils.java +++ b/app/src/main/java/com/gh/common/util/GameUtils.java @@ -225,6 +225,7 @@ public class GameUtils { gameUpdateEntity.setIndexPlugin(gameEntity.getIndexPlugin()); gameUpdateEntity.setPluginDesc(gameEntity.getPluginDesc()); gameUpdateEntity.setFormat(apkEntity.getFormat()); + gameUpdateEntity.setCurrentVersion(PackageUtils.getVersionNameByPackageName(apkEntity.getPackageName())); GameCollectionEntity pluggableCollection = getPluggableCollectionFromGameEntity(gameEntity, apkEntity.getPackageName()); if (pluggableCollection != null) { diff --git a/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt b/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt index 1e87908ee1..6d3ccef881 100644 --- a/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt +++ b/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt @@ -5,6 +5,9 @@ import com.gh.gamecenter.entity.HomePluggableFilterEntity import com.gh.gamecenter.room.AppDatabase import com.halo.assistant.HaloApp +/** + * 首页插件化区域辅助类 + */ object HomePluggableHelper { private val mHomePluggableFilterDao = AppDatabase.getInstance(HaloApp.getInstance().application).homePluggableFilterDao() @@ -56,7 +59,7 @@ object HomePluggableHelper { } } catch (e: Exception) { e.printStackTrace() - toastInInternalRelease("数据库/磁盘已满,插件筛选失败") + toastInInternalRelease("插件化筛选出现异常") } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/InstallUtils.java b/app/src/main/java/com/gh/common/util/InstallUtils.java index 4e8fb3fd42..0a438acd58 100644 --- a/app/src/main/java/com/gh/common/util/InstallUtils.java +++ b/app/src/main/java/com/gh/common/util/InstallUtils.java @@ -64,7 +64,7 @@ public class InstallUtils { keys.add(packageName); DownloadEntity downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByPackageName(packageName); - String installVersion = PackageUtils.getVersionByPackage(packageName); + String installVersion = PackageUtils.getVersionNameByPackageName(packageName); if (!TextUtils.isEmpty(installVersion) && downloadEntity != null && installVersion.equals(downloadEntity.getVersionName())) { if (!downloadEntity.isPluggable() || PackageUtils.isSignedByGh(context, packageName)) { diff --git a/app/src/main/java/com/gh/common/util/LogUtils.java b/app/src/main/java/com/gh/common/util/LogUtils.java index 7710b9e81f..259493edfd 100644 --- a/app/src/main/java/com/gh/common/util/LogUtils.java +++ b/app/src/main/java/com/gh/common/util/LogUtils.java @@ -262,7 +262,7 @@ public class LogUtils { Context context = HaloApp.getInstance().getApplication(); try { - object.put("version", PackageUtils.getVersionName()); + object.put("version", PackageUtils.getGhVersionName()); object.put("channel", HaloApp.getInstance().getChannel()); object.put("dia", MetaUtil.getBase64EncodedAndroidId()); object.put("time", Utils.getTime(context)); @@ -287,7 +287,7 @@ public class LogUtils { private static void uploadToReservation(JSONObject object) { Context context = HaloApp.getInstance().getApplication(); try { - object.put("version", PackageUtils.getVersionName()); + object.put("version", PackageUtils.getGhVersionName()); object.put("channel", HaloApp.getInstance().getChannel()); object.put("dia", MetaUtil.getBase64EncodedAndroidId()); object.put("time", Utils.getTime(context)); diff --git a/app/src/main/java/com/gh/common/util/NotificationHelper.kt b/app/src/main/java/com/gh/common/util/NotificationHelper.kt index 22a34e3717..9d89443464 100644 --- a/app/src/main/java/com/gh/common/util/NotificationHelper.kt +++ b/app/src/main/java/com/gh/common/util/NotificationHelper.kt @@ -15,7 +15,7 @@ object NotificationHelper { @JvmStatic fun showNotificationHintDialog(ugc: NotificationUgc, callBack: ((isShow: Boolean) -> Unit)? = null) { val showedNewVersion = SPUtils.getInt(Constants.SP_SHOWED_NOTIFICATION_NEW_VERSION, 0) - val currentVersion = PackageUtils.getVersionCode() + val currentVersion = PackageUtils.getGhVersionCode() // 版本升级后重置数据 if (currentVersion > showedNewVersion) { SPUtils.setBoolean(Constants.SP_SHOWED_NOTIFICATION_LOGIN, false) diff --git a/app/src/main/java/com/gh/common/util/PackageUtils.java b/app/src/main/java/com/gh/common/util/PackageUtils.java index a0c627bda1..c7eac0709d 100644 --- a/app/src/main/java/com/gh/common/util/PackageUtils.java +++ b/app/src/main/java/com/gh/common/util/PackageUtils.java @@ -87,6 +87,9 @@ public class PackageUtils { updateEntity.setIndexPlugin(gameEntity.getIndexPlugin()); updateEntity.setPluginDesc(gameEntity.getPluginDesc()); updateEntity.setFormat(apkEntity.getFormat()); + updateEntity.setSignature(apkEntity.getSignature()); + updateEntity.setCategory(gameEntity.getCategory()); + updateEntity.setCurrentVersion(PackageUtils.getVersionNameByPackageName(apkEntity.getPackageName())); updateList.add(updateEntity); } } @@ -97,7 +100,7 @@ public class PackageUtils { // ghVersion 不存在即是非插件游戏 if (TextUtils.isEmpty(apkEntity.getGhVersion())) { String versionFromRequest = apkEntity.getVersion(); - String versionFromInstalledApp = getVersionByPackage(apkEntity.getPackageName()); + String versionFromInstalledApp = getVersionNameByPackageName(apkEntity.getPackageName()); // 是否需要显示更新 boolean shouldShowUpdate = apkEntity.getForce(); @@ -125,6 +128,9 @@ public class PackageUtils { updateEntity.setIndexPlugin(gameEntity.getIndexPlugin()); updateEntity.setPluginDesc(gameEntity.getPluginDesc()); updateEntity.setFormat(apkEntity.getFormat()); + updateEntity.setSignature(apkEntity.getSignature()); + updateEntity.setCategory(gameEntity.getCategory()); + updateEntity.setCurrentVersion(PackageUtils.getVersionNameByPackageName(apkEntity.getPackageName())); updateList.add(updateEntity); } } @@ -438,21 +444,21 @@ public class PackageUtils { /* * 返回光环助手的版本信息 */ - public static String getVersionName() { + public static String getGhVersionName() { return BuildConfig.VERSION_NAME; } /* * 返回光环助手的版本code */ - public static int getVersionCode() { + public static int getGhVersionCode() { return BuildConfig.VERSION_CODE; } /* - * 获取apk的版本 + * 获取apk的 versionName */ - public static String getVersionByPackage(String packageName) { + public static String getVersionNameByPackageName(String packageName) { try { return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName; @@ -462,11 +468,24 @@ public class PackageUtils { return null; } + /* + * 获取apk的版本 versionCode + */ + public static int getVersionCodeByPackageName(String packageName) { + try { + return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName, + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode; + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + return 0; + } + /* - * 获取apk的版本 + * 获取应用的 icon */ - public static Drawable getIconByPackage(Context context, String packageName) { + public static Drawable getIconByPackageName(Context context, String packageName) { try { PackageManager packageManager = context.getApplicationContext().getPackageManager(); return packageManager.getApplicationIcon(packageName); @@ -630,7 +649,7 @@ public class PackageUtils { // ghVersion 不存在即是非插件游戏 if (TextUtils.isEmpty(apkEntity.getGhVersion())) { String versionFromRequest = apkEntity.getVersion(); - String versionFromInstalledApp = getVersionByPackage(apkEntity.getPackageName()); + String versionFromInstalledApp = getVersionNameByPackageName(apkEntity.getPackageName()); // 是否需要显示更新 boolean shouldShowUpdate = apkEntity.getForce(); diff --git a/app/src/main/java/com/gh/common/util/SpUtils.kt b/app/src/main/java/com/gh/common/util/SpUtils.kt index 0b7551873b..db004f46f2 100644 --- a/app/src/main/java/com/gh/common/util/SpUtils.kt +++ b/app/src/main/java/com/gh/common/util/SpUtils.kt @@ -23,7 +23,7 @@ object SPUtils { @JvmStatic fun getString(key: String): String { - return sp.getString(key, "")?:"" + return sp.getString(key, "") ?: "" } @JvmStatic @@ -81,19 +81,30 @@ object SPUtils { return context.getSharedPreferences("Halo", Context.MODE_PRIVATE).getBoolean(key, defaultValue) } + /** + * 使用传入的 SP 来存储 KV + */ @JvmStatic - fun setString(sp: SharedPreferences, key: String, value: String? = null) { + fun setString(sharedPreferences: SharedPreferences, key: String, value: String? = null) { try { - val commitStatus = sp.edit().putString(key, value).commit() + val commitStatus = sharedPreferences.edit().putString(key, value).commit() if (!commitStatus) { - sp.edit().putString(key, value).apply() + sharedPreferences.edit().putString(key, value).apply() } } catch (e: Exception) { e.printStackTrace() - sp.edit().putString(key, value).apply() + sharedPreferences.edit().putString(key, value).apply() } } + /** + * 使用传入的 SP 来读取 KV + */ + @JvmStatic + fun getString(sharedPreferences: SharedPreferences, key: String, value: String? = null): String { + return sharedPreferences.getString(key, "") ?: "" + } + @JvmStatic fun setStringSet(key: String, values: Set) { sp.edit().putStringSet(key, values).apply() diff --git a/app/src/main/java/com/gh/common/view/RichEditor.java b/app/src/main/java/com/gh/common/view/RichEditor.java index 0a80e4b6c7..fda2f83a94 100644 --- a/app/src/main/java/com/gh/common/view/RichEditor.java +++ b/app/src/main/java/com/gh/common/view/RichEditor.java @@ -11,7 +11,6 @@ import android.util.AttributeSet; import android.view.Gravity; import android.webkit.JavascriptInterface; import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -728,7 +727,7 @@ public class RichEditor extends WebView { @JavascriptInterface public int getAppVersionCode() { - return PackageUtils.getVersionCode(); + return PackageUtils.getGhVersionCode(); } /** diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index 3fd672a9e7..a6ad40c445 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -32,6 +32,7 @@ import com.gh.common.util.PackageUtils; import com.gh.common.util.PageSwitchDataHelper; import com.gh.common.util.SPUtils; import com.gh.gamecenter.BuildConfig; +import com.gh.gamecenter.download.DownloadedGameIdAndPackageNameDao; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.GameUpdateEntity; @@ -88,6 +89,7 @@ public class DownloadManager implements DownloadStatusListener { private ArrayMap downloadingMap; private DownloadDao mDownloadDao; + private DownloadedGameIdAndPackageNameDao mDownloadedGameIdAndPackageNameDao; private Set mUpdateMarks; @@ -132,6 +134,8 @@ public class DownloadManager implements DownloadStatusListener { public void onTaskDone(DownloadEntity entity) { downloadingMap.remove(entity.getUrl()); + mDownloadedGameIdAndPackageNameDao.add(entity.getGameId() + entity.getPackageName()); + if (downloadingMap.isEmpty()) { DownloadWorkManager.cancelWorker(); } @@ -145,6 +149,7 @@ public class DownloadManager implements DownloadStatusListener { private DownloadManager(Context context) { mContext = context.getApplicationContext(); mDownloadDao = DownloadDao.getInstance(mContext); + mDownloadedGameIdAndPackageNameDao = new DownloadedGameIdAndPackageNameDao(); mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK); diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index 80c1052ca5..3a4b18aa7c 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -203,7 +203,7 @@ public class MainActivity extends BaseActivity { mSp = PreferenceManager.getDefaultSharedPreferences(this); - isNewFirstLaunch = mSp.getBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), true); + isNewFirstLaunch = mSp.getBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), true); if (isNewFirstLaunch) { final LunchType lunchType = DeviceTokenUtils.getLaunchType(); // 延时两秒提交,避免提交时还没获取到 GID/OAID @@ -214,12 +214,11 @@ public class MainActivity extends BaseActivity { // 第一次打开App删除模拟器游戏记录(不包括更新版本) if (HaloApp.getInstance().isBrandNewInstall) { SimulatorGameManager.deleteAllSimulatorGame(); - } } }, 2000L); getPluginUpdate(); - mSp.edit().putBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), false).apply(); + mSp.edit().putBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), false).apply(); checkDevice(); // 根据设备信息判断用户是否是新用户 } diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java index 713450767c..3ba7d515d5 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java @@ -98,7 +98,7 @@ public class SplashScreenActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - mIsNewForThisVersion = mSharedPreferences.getBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), true); + mIsNewForThisVersion = mSharedPreferences.getBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), true); HaloApp.getInstance().isNewForThisVersion = mIsNewForThisVersion; super.onCreate(savedInstanceState); @@ -441,7 +441,7 @@ public class SplashScreenActivity extends BaseActivity { int index = name.indexOf("_"); if (index != -1) { String versionString = name.substring(name.indexOf("V") + 1, index); - Version currentVersion = new Version(PackageUtils.getVersionName()); + Version currentVersion = new Version(PackageUtils.getGhVersionName()); if (currentVersion.isHigherThan(versionString) || currentVersion.isEqual(versionString)) { file.delete(); } diff --git a/app/src/main/java/com/gh/gamecenter/SuggestionActivity.java b/app/src/main/java/com/gh/gamecenter/SuggestionActivity.java index e8589fb2e0..604f2fce15 100644 --- a/app/src/main/java/com/gh/gamecenter/SuggestionActivity.java +++ b/app/src/main/java/com/gh/gamecenter/SuggestionActivity.java @@ -1121,7 +1121,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall Map params = new HashMap<>(); params.put("from", mContactMethodEt.getText().toString()); - params.put("ghversion", PackageUtils.getVersionName()); + params.put("ghversion", PackageUtils.getGhVersionName()); params.put("channel", HaloApp.getInstance().getChannel()); params.put("type", android.os.Build.MODEL); params.put("sdk", String.valueOf(android.os.Build.VERSION.SDK_INT)); @@ -1151,7 +1151,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall Map params = new HashMap<>(); params.put("from", email); - params.put("ghversion", PackageUtils.getVersionName()); + params.put("ghversion", PackageUtils.getGhVersionName()); params.put("channel", HaloApp.getInstance().getChannel()); params.put("type", android.os.Build.MODEL); params.put("sdk", String.valueOf(android.os.Build.VERSION.SDK_INT)); diff --git a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt index e11ec51fd1..c91c12f4e8 100644 --- a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt @@ -15,6 +15,7 @@ import com.gh.common.util.visibleIf import com.gh.download.DownloadManager import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.TabItemDownloadNumberBinding import com.gh.gamecenter.entity.HomePluggableFilterEntity import com.gh.gamecenter.entity.PluginLocation import com.gh.gamecenter.eventbus.EBDownloadChanged @@ -30,46 +31,44 @@ import org.greenrobot.eventbus.ThreadMode class DownloadFragment : BaseFragment_TabLayout() { - companion object { - const val INDEX_DOWNLOAD = 0 - const val INDEX_UPDATE = 1 - } - // 下载 tab 是否被手动选中过 private var mIsDownloadTabHasBeenSelected: Boolean = false private var mIsUpdateTabHasBeenSelected: Boolean = false - private lateinit var mDownloadNumber: TextView - private lateinit var mUpdateNumber: TextView - private lateinit var mDownloadManager: DownloadManager + private lateinit var mDownloadNumberTv: TextView + private lateinit var mUpdateNumberTv: TextView + private val mDownloadManager: DownloadManager by lazy { DownloadManager.getInstance(HaloApp.getInstance().application) } private var mPermanentInactivePluggableApkList: List? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mDownloadManager = DownloadManager.getInstance(HaloApp.getInstance().application) mPermanentInactivePluggableApkList = HomePluggableHelper.getPermanentInactivePluggablePackage() } override fun initFragmentList(fragments: MutableList) { fragments.add(GameDownloadFragment()) fragments.add(GameUpdateFragment()) + fragments.add(UpdatableGameFragment()) fragments.add(InstalledGameFragment()) } override fun initTabTitleList(tabTitleList: MutableList) { tabTitleList.add(getString(R.string.download_game)) tabTitleList.add(getString(R.string.download_tab_update)) - tabTitleList.add("已安装") + tabTitleList.add(getString(R.string.download_tab_update)) + tabTitleList.add(getString(R.string.download_tab_installed)) } override fun provideTabView(position: Int, tabTitle: String): View? { - var view = LayoutInflater.from(context).inflate(R.layout.tab_item_download_number, null) - (view.findViewById(R.id.tab_title) as TextView).text = tabTitle - when { - INDEX_DOWNLOAD == position -> mDownloadNumber = view.findViewById(R.id.tab_download_number) - INDEX_UPDATE == position -> mUpdateNumber = view.findViewById(R.id.tab_download_number) + val viewBinding = TabItemDownloadNumberBinding.inflate(LayoutInflater.from(context)) + var view: View? = viewBinding.root + viewBinding.tabTitle.text = tabTitle + + when (position) { + INDEX_DOWNLOAD -> mDownloadNumberTv = viewBinding.tabDownloadNumber + INDEX_UPDATE -> mUpdateNumberTv = viewBinding.tabDownloadNumber else -> view = null } return view @@ -86,7 +85,7 @@ class DownloadFragment : BaseFragment_TabLayout() { updateUpdateHint() updateDownloadHint() - if (mDownloadNumber.visibility != View.VISIBLE && mUpdateNumber.visibility == View.VISIBLE) { + if (mDownloadNumberTv.visibility != View.VISIBLE && mUpdateNumberTv.visibility == View.VISIBLE) { mViewPager.currentItem = INDEX_UPDATE } else { // 进入并选中下载管理时立马标记已下载待安装为已读 @@ -116,12 +115,6 @@ class DownloadFragment : BaseFragment_TabLayout() { override fun onPageSelected(index: Int) { EventBus.getDefault().post(EBUISwitch(DownloadManagerActivity.TAG, index)) - val tabName = when (index) { - INDEX_DOWNLOAD -> "游戏下载" - INDEX_UPDATE -> "游戏更新" - else -> "已安装" - } - if (index == INDEX_UPDATE) { mIsUpdateTabHasBeenSelected = true updateUpdateHint() @@ -131,8 +124,6 @@ class DownloadFragment : BaseFragment_TabLayout() { updateDownloadHint() mDownloadManager.markDownloadingTaskAsRead() } - -// MtaHelper.onEvent("下载管理", "Tab", tabName) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -153,13 +144,13 @@ class DownloadFragment : BaseFragment_TabLayout() { } private fun updateDownloadHint() { - if (!::mDownloadNumber.isInitialized || !isAdded) return + if (!::mDownloadNumberTv.isInitialized || !isAdded) return val downloadData = DownloadManager.getInstance(context).allDownloadEntityExcludeSilentUpdate if (downloadData.size > 0) { - mDownloadNumber.visibility = View.VISIBLE + mDownloadNumberTv.visibility = View.VISIBLE } else { - mDownloadNumber.visibility = View.INVISIBLE + mDownloadNumberTv.visibility = View.INVISIBLE return } @@ -170,32 +161,32 @@ class DownloadFragment : BaseFragment_TabLayout() { } } - val layoutParams = mDownloadNumber.layoutParams as LinearLayout.LayoutParams + val layoutParams = mDownloadNumberTv.layoutParams as LinearLayout.LayoutParams when { downloadingCount > 0 -> { layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT layoutParams.setMargins(4F.dip2px(), 0, 0, 0F.dip2px()) - mDownloadNumber.setBackgroundColor(Color.TRANSPARENT) - mDownloadNumber.text = downloadingCount.toString() + mDownloadNumberTv.setBackgroundColor(Color.TRANSPARENT) + mDownloadNumberTv.text = downloadingCount.toString() } mDownloadManager.isContainsUnreadDownloadedTask -> { layoutParams.width = 6F.dip2px() layoutParams.height = 6F.dip2px() layoutParams.setMargins(2F.dip2px(), 0, 0, 3F.dip2px()) - mDownloadNumber.setBackgroundResource(R.drawable.oval_hint_red_bg) - mDownloadNumber.text = "" + mDownloadNumberTv.setBackgroundResource(R.drawable.oval_hint_red_bg) + mDownloadNumberTv.text = "" } - else -> mDownloadNumber.visibility = View.GONE + else -> mDownloadNumberTv.visibility = View.GONE } - mDownloadNumber.layoutParams = layoutParams + mDownloadNumberTv.layoutParams = layoutParams } private fun updateUpdateHint() { - if (!::mUpdateNumber.isInitialized) return + if (!::mUpdateNumberTv.isInitialized) return if (mIsUpdateTabHasBeenSelected) { - mUpdateNumber.visibility = View.INVISIBLE + mUpdateNumberTv.visibility = View.INVISIBLE return } @@ -205,7 +196,7 @@ class DownloadFragment : BaseFragment_TabLayout() { && mPermanentInactivePluggableApkList?.filter { it.pkgName == gameUpdateEntity.packageName }?.size == 0 }) - mUpdateNumber.visibleIf(updateCount != 0) + mUpdateNumberTv.visibleIf(updateCount != 0) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -230,4 +221,9 @@ class DownloadFragment : BaseFragment_TabLayout() { } } + companion object { + const val INDEX_DOWNLOAD = 0 + const val INDEX_UPDATE = 1 + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/download/DownloadedGameIdAndPackageNameDao.kt b/app/src/main/java/com/gh/gamecenter/download/DownloadedGameIdAndPackageNameDao.kt new file mode 100644 index 0000000000..1d5ccce3c8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/download/DownloadedGameIdAndPackageNameDao.kt @@ -0,0 +1,14 @@ +package com.gh.gamecenter.download + +import com.gh.base.BaseSimpleDao + +/** + * 用来记录光环下载过什么包名和游戏ID件的一个简单类 + */ +class DownloadedGameIdAndPackageNameDao : BaseSimpleDao() { + + override fun getSPKey() = "downloaded_game_id_and_packagename" + + override fun getMaxSize(): Int = 1000 + +} \ No newline at end of file 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 a894121c6a..8b72fc1079 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java @@ -40,6 +40,7 @@ import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import butterknife.BindView; import butterknife.OnClick; @@ -65,6 +66,8 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi TextView mNoDataSkipBtn; @BindView(R.id.downloadmanager_tv_allstart) TextView mDownloadmanagerAllstartTv; + @BindView(R.id.downloadingHintContainer) + View mDownloadingHintContainer; GameDownloadFragmentAdapter adapter; RelativeLayout.LayoutParams rparams; @@ -235,6 +238,13 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi mNoDataSkipHintTv.setText("暂无下载"); mNoDataSkipBtn.setText("去首页看看"); mNoDataSkipBtn.setOnClickListener(v -> MainActivity.skipToMainActivity(getActivity(), MainWrapperFragment.INDEX_HOME)); + mDownloadingHintContainer.setOnClickListener(v -> { + int downloadingPosition = adapter.getDoneList().size() + 2; + + if (adapter.getItemCount() > downloadingPosition) { + mDownloadmanagerRv.scrollToPosition(downloadingPosition); + } + }); mDownloadmanagerRv.setHasFixedSize(true); adapter = new GameDownloadFragmentAdapter(getActivity(), mNoDataSkip, url); @@ -282,6 +292,7 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi rparams.topMargin = 0; mDownloadmanagerHeadRl.setLayoutParams(rparams); } + showScrollToDownloadContainerIfNeeded(); } }); mDownloadmanagerRv.setOnDispatchTouchListener((v, event) -> { @@ -302,6 +313,7 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi adapter.initMap(); DownloadManager.getInstance(getActivity()).addObserver(dataWatcher); adapter.notifyDataSetChanged(); + showScrollToDownloadContainerIfNeeded(); if (adapter.getDownloadingList().isEmpty() && adapter.getDoneList().isEmpty()) { mNoDataSkip.setVisibility(View.VISIBLE); @@ -441,4 +453,18 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi mDownloadmanagerAllstartTv.setTextColor(ContextCompat.getColor(getContext(), R.color.btn_gray)); } + private void showScrollToDownloadContainerIfNeeded() { + LinearLayoutManager layoutManager = (LinearLayoutManager) mDownloadmanagerRv.getLayoutManager(); + if (layoutManager != null) { + int lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition(); + + if (adapter.getDownloadingList().size() != 0 + && adapter.getDoneList().size() >= lastCompletelyVisibleItemPosition) { + mDownloadingHintContainer.setVisibility(View.VISIBLE); + } else { + mDownloadingHintContainer.setVisibility(View.GONE); + } + } + } + } diff --git a/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java index d3a26310c3..69362595d4 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java @@ -341,7 +341,7 @@ class GameUpdateFragmentAdapter extends BaseRecyclerAdapter implemen viewHolder.guName.postDelayed(() -> viewHolder.guName.setSelected(true), 2000); } - String currentVersion = PackageUtils.getVersionByPackage(updateEntity.getPackageName()); + String currentVersion = PackageUtils.getVersionNameByPackageName(updateEntity.getPackageName()); if (TextUtils.isEmpty(currentVersion)) { viewHolder.guCurrent.setText("当前:无"); } else { diff --git a/app/src/main/java/com/gh/gamecenter/download/InstalledGameFragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/download/InstalledGameFragmentAdapter.java index e82bacf7ac..99bd30459f 100644 --- a/app/src/main/java/com/gh/gamecenter/download/InstalledGameFragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/download/InstalledGameFragmentAdapter.java @@ -308,12 +308,12 @@ public class InstalledGameFragmentAdapter extends BaseRecyclerAdapter 0) { name = String.format("%s - %s", gameEntity.getName(), PlatformUtils.getInstance(mContext).getPlatformName(gameEntity.getApk().get(0).getPlatform())); - binding.gameIconIv.setImageDrawable(PackageUtils.getIconByPackage(mContext, gameEntity.getApk().get(0).getPackageName())); + binding.gameIconIv.setImageDrawable(PackageUtils.getIconByPackageName(mContext, gameEntity.getApk().get(0).getPackageName())); binding.gameIconDecoratorIv.setVisibility(View.GONE); if (SimulatorGameManager.isSimulatorGame(gameEntity)) { binding.gameDes.setText(String.format("V%s", gameEntity.getApk().get(0).getVersion())); } else { - binding.gameDes.setText(String.format("V%s", PackageUtils.getVersionByPackage(gameEntity.getApk().get(0).getPackageName()))); + binding.gameDes.setText(String.format("V%s", PackageUtils.getVersionNameByPackageName(gameEntity.getApk().get(0).getPackageName()))); } } else { name = gameEntity.getName(); diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt new file mode 100644 index 0000000000..8d29158c12 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt @@ -0,0 +1,428 @@ +package com.gh.gamecenter.download + +import android.annotation.SuppressLint +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.view.setPadding +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.ExposureEvent +import com.gh.common.exposure.ExposureEvent.Companion.createEvent +import com.gh.common.exposure.ExposureSource +import com.gh.common.exposure.IExposable +import com.gh.common.util.* +import com.gh.common.view.BugFixedPopupWindow +import com.gh.download.DownloadManager +import com.gh.download.dialog.DownloadDialog +import com.gh.gamecenter.DownloadManagerActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.* +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.eventbus.EBSkip +import com.gh.gamecenter.home.BlankDividerViewHolder +import com.gh.gamecenter.manager.PackagesManager +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import org.greenrobot.eventbus.EventBus + +class UpdatableGameAdapter(private var mViewModel: UpdatableGameViewModel) : + RecyclerView.Adapter(), IExposable { + + private var mItemList: ArrayList? = null + private val mExposureSource by lazy { + ArrayList().apply { + add(ExposureSource("下载管理")) + add(ExposureSource("游戏更新")) + } + } + + fun submitList(dataList: ArrayList) { + mItemList = dataList + notifyDataSetChanged() + } + + override fun getItemViewType(position: Int): Int { + val item = mItemList!![position] + return when { + item.header != null || item.ignoredUpdateHeader != null -> TYPE_HEADER + item.normalUpdate != null || item.ignoredUpdate != null -> TYPE_NORMAL_GAME + item.normalUpdateWithArrow != null -> TYPE_NORMAL_GAME_WITH_ARROW + item.otherVersionUpdateHint != null -> TYPE_OTHER_VERSION_GAME_HINT + item.otherVersionUpdate != null -> TYPE_OTHER_VERSION_GAME + item.divider != null -> TYPE_DIVIDER + else -> TYPE_OTHER_VERSION_GAME + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + TYPE_HEADER -> UpdatableHeaderViewHolder(parent.toBinding()) + TYPE_NORMAL_GAME -> UpdatableGameViewHolder(parent.toBinding()) + TYPE_NORMAL_GAME_WITH_ARROW -> UpdatableGameViewHolder(parent.toBinding()) + TYPE_OTHER_VERSION_GAME -> UpdatableGameViewHolder(parent.toBinding()) + TYPE_OTHER_VERSION_GAME_HINT -> UpdatableOtherVersionGameHintViewHolder(parent.toBinding()) + TYPE_DIVIDER -> BlankDividerViewHolder(parent.toBinding()).also { + it.binding.container.layoutParams = + it.binding.container.layoutParams.apply { + height = 8F.dip2px() + } + } + else -> BlankDividerViewHolder(parent.toBinding()) + } + } + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val itemData = mItemList?.get(position)!! + when (holder) { + is UpdatableHeaderViewHolder -> { + itemData.let { + holder.binding.updateAllBtn.enlargeTouchArea() + holder.binding.updateAllBtn.goneIf(!itemData.miscShowUpdateAll) + holder.binding.updateAllBtn.text = itemData.miscUpdateText + holder.binding.updateAllBtn.setOnClickListener { + if (itemData.miscUpdateText == "全部更新") { + mViewModel.updateAllMatchedVersionValidUpdate() + } + } + holder.binding.infoTv.text = itemData.header ?: itemData.ignoredUpdateHeader + if (itemData.header != null) { + holder.binding.infoTv.setTextColor(R.color.text_333333.toColor()) + holder.binding.root.setOnClickListener(null) + holder.binding.infoTv.setCompoundDrawablesWithIntrinsicBounds( + null, + null, + null, + null + ) + } else { + holder.binding.infoTv.setTextColor(R.color.text_999999.toColor()) + holder.binding.root.setOnClickListener { + mViewModel.toggleIgnoredUpdateVisibility() + } + holder.binding.infoTv.setCompoundDrawablesWithIntrinsicBounds( + null, + null, + if (mViewModel.isIgnoredUpdateExpanded()) { + R.drawable.ic_arrow_up_grey.toDrawable() + } else { + R.drawable.ic_arrow_down_grey.toDrawable() + }, + null + ) + } + } + } + is BlankDividerViewHolder -> { + if (itemData.divider == UpdatableGameViewModel.GREY) { + holder.binding.container.setBackgroundColor(R.color.text_F5F5F5.toColor()) + } else { + holder.binding.container.setBackgroundColor(Color.WHITE) + } + } + is UpdatableGameViewHolder -> { + val update: GameUpdateEntity = (itemData.normalUpdate + ?: itemData.normalUpdateWithArrow + ?: itemData.otherVersionUpdate + ?: itemData.ignoredUpdate)!! + + val context = holder.binding.root.context + val pluginDesc: String = update.pluginDesc.take(3) + val downloadManager = DownloadManager.getInstance(context) + + holder.binding.run { + otherVersionHintIv.goneIf(itemData.normalUpdateWithArrow == null) + + container.setOnClickListener { + DirectUtils.directToGameDetail( + context = context, + id = update.id, + entrance = mViewModel.entrance, + traceEvent = update.exposureEvent + ) + } + + if (itemData.otherVersionUpdate != null) { + container.setBackgroundColor(R.color.bg_F2F7FC.toColor()) + } else { + container.setBackgroundColor(Color.WHITE) + } + + iconIv.displayGameIcon(update.rawIcon ?: update.icon, update.iconSubscript) + val nameText = if (!update.readablePlatform.isNullOrEmpty()) { + update.name + " - " + update.readablePlatform + } else { + update.name + } + nameTv.text = nameText + currentVersionTv.text = "当前:V${update.currentVersion}" + newVersionTv.text = "新版:V${update.version} | ${update.size}" + optionIv.setOnClickListener { + showIgnoreOption(it, update, itemData.ignoredUpdate != null) + } + + val downloadEntity = downloadManager.getDownloadEntityByUrl(update.url) + updateUpdateBtn(updateBtn, downloadEntity, update, pluginDesc, downloadManager) + } + + generateExposureEvent(update) + } + is UpdatableOtherVersionGameHintViewHolder -> { + holder.binding.root.setOnClickListener { + mViewModel.toggleOtherVersionVisibility(itemData.miscPackageName) + } + if (itemData.otherVersionUpdateHint == true) { + holder.binding.selectorTv.setCompoundDrawablesWithIntrinsicBounds( + null, + null, + R.drawable.ic_arrow_up_blue.toDrawable(), + null + ) + } else { + holder.binding.selectorTv.setCompoundDrawablesWithIntrinsicBounds( + null, + null, + R.drawable.ic_arrow_down_blue.toDrawable(), + null + ) + } + holder.binding.closeHintTv.enlargeTouchArea() + holder.binding.closeHintTv.setOnClickListener { + DialogHelper.showDialog( + it.context, + title = "关闭提示", + content = "关闭提示后,将不再提示游戏其他版本的更新", + cancelText = "永不更新", + confirmText = "仅此次", + cancelClickCallback = { + mViewModel.suppressUpdate( + packageName = itemData.miscPackageName, + currentUpdatableVersion = itemData.miscVersion, + true + ) + }, + confirmClickCallback = { + mViewModel.suppressUpdate( + packageName = itemData.miscPackageName, + currentUpdatableVersion = itemData.miscVersion, + false + ) + }, + extraConfig = DialogHelper.Config(showCloseIcon = true) + ) + } + } + } + } + + override fun getItemCount() = mItemList?.size ?: 0 + + override fun getEventByPosition(pos: Int): ExposureEvent? { + return mItemList?.get(pos)?.normalUpdate?.exposureEvent + ?: mItemList?.get(pos)?.normalUpdateWithArrow?.exposureEvent + ?: mItemList?.get(pos)?.otherVersionUpdate?.exposureEvent + ?: mItemList?.get(pos)?.ignoredUpdate?.exposureEvent + } + + override fun getEventListByPosition(pos: Int): List? = null + + private fun showIgnoreOption(view: View, update: GameUpdateEntity, isUpdateIgnored: Boolean) { + val inflater = LayoutInflater.from(view.context) + val mainBinding = LayoutPopupContainerBinding.inflate(inflater) + val popupWindow = BugFixedPopupWindow( + mainBinding.root, + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + + LayoutPopupOptionItemBinding.inflate(inflater, mainBinding.container, false).apply { + root.layoutParams = root.layoutParams.apply { + width = 92F.dip2px() + height = 44F.dip2px() + } + hintText.setPadding(0) + if (isUpdateIgnored) { + hintText.text = "取消忽略" + } else { + hintText.text = "忽略更新" + } + + root.setOnClickListener { + if (isUpdateIgnored) { + mViewModel.undoIgnoredUpdate(update.packageName) + } else { + mViewModel.ignoreUpdate(update.packageName) + } + popupWindow.dismiss() + } + mainBinding.container.addView(root) + } + + popupWindow.isTouchable = true + popupWindow.isFocusable = true + + popupWindow.showAutoOrientation(view) + } + + private fun updateUpdateBtn( + updateBtn: TextView, + downloadEntity: DownloadEntity?, + update: GameUpdateEntity, + pluginDesc: String, + downloadManager: DownloadManager + ) { + if (downloadEntity == null) { + if (PackagesManager.isCanUpdate(update.id, update.packageName)) { + updateBtn.setText(R.string.update) + updateBtn.setTextColor(Color.WHITE) + updateBtn.setBackgroundResource(R.drawable.download_button_normal_style) + } else if (update.isPluggable) { + updateBtn.text = "${pluginDesc}化" + updateBtn.setTextColor(Color.WHITE) + val samePackageNameDownloadEntity = + downloadManager.getDownloadEntityByPackageName(update.packageName) + if (samePackageNameDownloadEntity == null || samePackageNameDownloadEntity.url == update.url) { + updateBtn.isClickable = true + updateBtn.setBackgroundResource(R.drawable.download_button_pluggable_style) + } else { + updateBtn.isClickable = false + updateBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up) + } + } else { + updateBtn.setText(R.string.launch) + updateBtn.setTextColor(R.color.theme_font.toColor()) + updateBtn.setBackgroundResource(R.drawable.detail_downloading_normal_style) + } + } else { + if (update.isPluggable) { + if (downloadEntity.status == DownloadStatus.done) { + updateBtn.setText(R.string.install) + updateBtn.setTextColor(Color.WHITE) + updateBtn.setBackgroundResource(R.drawable.download_button_pluggable_style) + } else { + updateBtn.setText(R.string.downloading) + updateBtn.setTextColor( + ContextCompat.getColorStateList( + updateBtn.context, + R.color.text_downloading_style + ) + ) + updateBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style) + } + } else { + if (downloadEntity.status == DownloadStatus.done) { + updateBtn.setText(R.string.install) + updateBtn.setTextColor(Color.WHITE) + updateBtn.setBackgroundResource(R.drawable.download_button_normal_style) + } else { + updateBtn.setText(R.string.downloading) + updateBtn.setTextColor( + ContextCompat.getColorStateList( + updateBtn.context, + R.color.text_downloading_style + ) + ) + updateBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style) + } + } + } + + updateBtn.setOnClickListener { + val str: String = updateBtn.text.toString() + if ("更新" == str || str.contains("化")) { + (updateBtn.context as FragmentActivity).checkStoragePermissionBeforeAction { + DialogUtils.checkDownload( + updateBtn.context, + update.size + ) { isSubscribe: Boolean -> + if (str.contains("化")) { + if (update.pluggableCollection != null) { + DownloadDialog.showDownloadDialog( + updateBtn.context, + update.transformGameEntity(), + update.exposureEvent, + mViewModel.entrance, + pluginDesc + "化:" + update.name + ) + return@checkDownload + } else { + updateBtn.setText(R.string.downloading) + updateBtn.setTextColor( + ContextCompat.getColorStateList( + updateBtn.context, + R.color.text_plugining_style + ) + ) + updateBtn.setBackgroundResource(R.drawable.game_item_btn_plugining_style) + } + } else { + updateBtn.setText(R.string.downloading) + updateBtn.setTextColor( + ContextCompat.getColorStateList( + updateBtn.context, + R.color.text_downloading_style + ) + ) + updateBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style) + } + + mViewModel.update(update, isSubscribe) + mViewModel.refreshList() + EventBus.getDefault() + .post( + EBSkip( + DownloadManagerActivity.TAG, + DownloadManagerActivity.INDEX_DOWNLOAD + ) + ) + } + } + } else if (updateBtn.context.getString(R.string.launch) == str) { + PackageUtils.launchApplicationByPackageName(updateBtn.context, update.packageName) + } else if (updateBtn.context.getString(R.string.downloading) == str) { + mViewModel.refreshList() + EventBus.getDefault().post( + EBSkip( + DownloadManagerActivity.TAG, + DownloadManagerActivity.INDEX_DOWNLOAD + ) + ) + } else if (updateBtn.context.getString(R.string.install) == str) { + PackageInstaller.install( + updateBtn.context, + DownloadManager.getInstance(updateBtn.context) + .getDownloadEntityByUrl(update.url) + ) + } + } + } + + private fun generateExposureEvent(updateEntity: GameUpdateEntity) { + updateEntity.exposureEvent = + createEvent(updateEntity.transformGameEntity(), mExposureSource) + } + + companion object { + const val TYPE_DIVIDER = 233 + const val TYPE_HEADER = 234 + const val TYPE_NORMAL_GAME = 235 + const val TYPE_NORMAL_GAME_WITH_ARROW = 236 + const val TYPE_OTHER_VERSION_GAME = 237 + const val TYPE_OTHER_VERSION_GAME_HINT = 238 + } + + class UpdatableHeaderViewHolder(val binding: ItemUpdatableGameHeaderBinding) : + RecyclerView.ViewHolder(binding.root) + + class UpdatableGameViewHolder(val binding: ItemUpdatableGameBinding) : + RecyclerView.ViewHolder(binding.root) + + class UpdatableOtherVersionGameHintViewHolder(val binding: ItemUpdatableOtherGameHintBinding) : + RecyclerView.ViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt new file mode 100644 index 0000000000..86d3a5ce01 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt @@ -0,0 +1,81 @@ +package com.gh.gamecenter.download + +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import com.gh.base.fragment.LazyFragment +import com.gh.common.exposure.ExposureListener +import com.gh.common.util.EntranceUtils +import com.gh.common.util.observeNonNull +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.MainActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentGameUpdatableBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBReuse +import com.gh.gamecenter.fragment.MainWrapperFragment +import com.gh.gamecenter.packagehelper.PackageViewModel +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class UpdatableGameFragment : LazyFragment() { + + private var mViewModel: UpdatableGameViewModel? = null + private var mPackageViewModel: PackageViewModel? = null + private var mBinding: FragmentGameUpdatableBinding? = null + private val mAdapter: UpdatableGameAdapter by lazy { UpdatableGameAdapter(mViewModel!!) } + + override fun getRealLayoutId() = R.layout.fragment_game_updatable + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentGameUpdatableBinding.bind(inflatedView) + } + + override fun onFragmentFirstVisible() { + val packageName = activity?.intent?.getStringExtra(EntranceUtils.KEY_PACKAGENAME) ?: "" + val entrance = activity?.intent?.getStringExtra(EntranceUtils.KEY_ENTRANCE) ?: "" + + mViewModel = viewModelProvider(UpdatableGameViewModel.Factory(packageName, entrance)) + + super.onFragmentFirstVisible() + + mPackageViewModel = viewModelProvider(PackageViewModel.Factory()) + mPackageViewModel?.getGameUpdateIncludeCurrentVersion() + ?.observeNonNull(viewLifecycleOwner) { updatableList -> + mViewModel?.setUpdatableList(updatableList) + } + mViewModel?.updatableData?.observe(viewLifecycleOwner) { + mAdapter.submitList(it) + } + } + + override fun initRealView() { + super.initRealView() + + mBinding?.run { + noDataContainer.reuseNodataSkipTvHint.text = "暂无更新" + noDataContainer.reuseNodataSkipTvBtn.text = "去首页看看" + noDataContainer.reuseNodataSkipTvBtn.setOnClickListener { + MainActivity.skipToMainActivity(activity, MainWrapperFragment.INDEX_HOME) + } + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + recyclerView.adapter = mAdapter.also { + recyclerView.addOnScrollListener(ExposureListener(this@UpdatableGameFragment, it)) + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + if ("delete" == status.status || "download" == status.status || "done" == status.status) { + mViewModel?.refreshList() + } + } + + // 更新平台信息 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(reuse: EBReuse) { + if ("PlatformChanged" == reuse.type && isAdded) { + mViewModel?.refreshList() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt new file mode 100644 index 0000000000..639170c88d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt @@ -0,0 +1,638 @@ +package com.gh.gamecenter.download + +import android.app.Application +import android.view.View +import androidx.lifecycle.* +import com.gh.base.BaseSimpleDao +import com.gh.common.constant.Constants +import com.gh.common.exposure.ExposureUtils +import com.gh.common.exposure.ExposureUtils.logADownloadExposureEvent +import com.gh.common.history.HistoryHelper.insertGameEntity +import com.gh.common.util.* +import com.gh.common.util.GsonUtils.toJson +import com.gh.common.util.PackageInstaller.createDownloadId +import com.gh.common.util.PackageInstaller.getDownloadPathWithId +import com.gh.download.DownloadManager +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.entity.PluginLocation +import com.gh.gamecenter.eventbus.EBDownloadChanged +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import org.greenrobot.eventbus.EventBus +import java.util.* +import kotlin.collections.ArrayList + +class UpdatableGameViewModel( + application: Application, + private var mSpecialPackageName: String, + var entrance: String +) : AndroidViewModel(application) { + + private var mRawUpdatableList = ArrayList() + private var mUpdatableItemDataList = ArrayList() // 最终暴露给 View 层装饰好的数据 + + private var mPackageUpdateList = ArrayList() // 包名更新列表,包括了我的版本和其它版本 (如果存在的话) + + private var mUpdatableListLiveData = MutableLiveData>() + + private val mSuppressUpdateDao by lazy { SuppressUpdateDao() } + private val mIgnoredUpdateDao by lazy { IgnoredUpdateDao() } + private val mDownloadedGameIdAndPackageNameDao by lazy { DownloadedGameIdAndPackageNameDao() } + + // 缓存的匹配的游戏列表,列表刷新是会同时更新 + private val mCachedMatchedVersionValidUpdateList by lazy { arrayListOf() } + + private val mOtherVersionExpandedMap by lazy { hashMapOf() } + private var mShouldShowIgnoredUpdate = false + + var updatableData: LiveData> = mUpdatableListLiveData + + /** + * 更新可更新数据列表 + */ + fun setUpdatableList(updatableList: List) { + if (mRawUpdatableList != updatableList) { + mRawUpdatableList.clear() + + for (update in updatableList) { + // 筛选仅下载管理出现的插件化更新 + if (update.isShowPlugin(PluginLocation.only_index)) { + val platform = + PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform) + if (!platform.isNullOrEmpty() && "官方版" != platform) { + update.readablePlatform = platform + } + + mRawUpdatableList.add(update) + } + } + } + + mUpdatableItemDataList.clear() + + sortUpdatableListByType(mRawUpdatableList) + + mPackageUpdateList = constructPackageUpdateList(mRawUpdatableList) + + mUpdatableItemDataList = transformUpdatableListIntoUpdatableItemDataList(mPackageUpdateList) + + if (mRawUpdatableList.isNotEmpty()) { + // TODO 父子 Fragment 共享同一个 ViewModel 来实现数据交流 + EventBus.getDefault().post( + EBDownloadChanged( + "update", + View.VISIBLE, + mCachedMatchedVersionValidUpdateList.size + ) + ) + } + + if (mSpecialPackageName.isNotEmpty()) { + var isAlreadyExisted = false + for (update in mRawUpdatableList) { + if (update.packageName == mSpecialPackageName) { + isAlreadyExisted = true + break + } + } + if (!isAlreadyExisted) { + getUpdateInfoByPackageName(mSpecialPackageName) + } + } + + mUpdatableListLiveData.postValue(mUpdatableItemDataList) + } + + /** + * 构建我的版本和其它版本列表(供后续修改处理) + */ + private fun constructPackageUpdateList(updatableList: ArrayList) + : ArrayList { + val packageNameAndUpdateListMap = hashMapOf>() + val packageUpdateList: ArrayList = arrayListOf() + + // 按包名分 + for (update in updatableList) { + var list = packageNameAndUpdateListMap[update.packageName] + if (list == null) { + list = arrayListOf() + packageNameAndUpdateListMap[update.packageName] = list + } + list.add(update) + } + + // 找到我的版本 + for ((packageName, list) in packageNameAndUpdateListMap) { + val sign = PackageUtils.getApkSignatureByPackageName(getApplication(), packageName) + val isSignByGh = PackageUtils.isSignedByGh(getApplication(), packageName) + val ghId = if (isSignByGh) PackageUtils.getGhId(packageName) else null + val versionName = PackageUtils.getVersionNameByPackageName(packageName) ?: "" + + var matchedVersionUpdate: GameUpdateEntity? = null + val mismatchedVersionUpdateList: ArrayList = arrayListOf() + + // 已安装的游戏为插件游戏,找插件包里的 GH_ID 跟我的版本一样的作为我的版本 + if (isSignByGh && ghId != null) { + for (update in list) { + if (update.id == ghId) { + matchedVersionUpdate = update + } else { + mismatchedVersionUpdateList.add(update) + } + } + } else if (mDownloadedGameIdAndPackageNameDao.contains(packageName)) { + // 光环曾经下载过对应包名的游戏,找到对应游戏ID的更新作为我的版本 + val idAndPackageNameString = mDownloadedGameIdAndPackageNameDao.getRawString() + for (update in list) { + if (idAndPackageNameString.contains(update.id)) { + matchedVersionUpdate = update + } else { + mismatchedVersionUpdateList.add(update) + } + } + } else if (list.find { it.signature == sign } != null) { + // 存在同包名同签名的游戏,以同包名同签名的游戏作为我的版本 + for (update in list) { + if (sign == update.signature) { + matchedVersionUpdate = update + } else { + mismatchedVersionUpdateList.add(update) + } + } + } else { + // 以同包名中七天下载量最高的游戏作为我的版本 + for (update in list) { + if (update.download >= matchedVersionUpdate?.download ?: 0) { + matchedVersionUpdate = update + } + + mismatchedVersionUpdateList.add(update) + } + + mismatchedVersionUpdateList.remove(matchedVersionUpdate) + mismatchedVersionUpdateList.sortBy { it.download } + } + + // 清理 mismatchedVersionUpdateList 的同版本数据,同版本只在找不到 matchedVersion 的时候作替补用 + val iterator = mismatchedVersionUpdateList.iterator() + while (iterator.hasNext()) { + val update = iterator.next() + if (update.version == versionName) { + iterator.remove() + continue + } + } + + // TODO 我的版本应该不会为空吧? + packageUpdateList.add( + PackageUpdate( + matchedVersionUpdate!!, + mismatchedVersionUpdateList + ) + ) + } + return packageUpdateList + } + + /** + * 将我的版本和其它版本列表变形成列表可显示的数据 + */ + private fun transformUpdatableListIntoUpdatableItemDataList(packageUpdateList: ArrayList) + : ArrayList { + val updatableDataItemList = arrayListOf() + + val validPackageUpdateList: ArrayList = arrayListOf() // 按钮为更新的我的游戏列表 + val invalidPackageUpdateList: ArrayList = arrayListOf() // 按钮为启动的我的游戏列表 + val ignoredPackageUpdateList: ArrayList = arrayListOf() // 被隐藏的我的游戏列表 + + val permanentSuppressedUpdatePackageNameList = mSuppressUpdateDao.getAll() + val ignoredUpdatePackageNameList = mIgnoredUpdateDao.getAll() + + mCachedMatchedVersionValidUpdateList.clear() + + for (packageUpdate in packageUpdateList) { + val packageName = packageUpdate.matchedVersionUpdate.packageName + val currentlyUpdatableVersion = packageUpdate.matchedVersionUpdate.currentVersion + + // 类型不为单机游戏/gj单机的游戏或选择了临时隐藏或永久隐藏的不需要显示其它版本 + if ((packageUpdate.matchedVersionUpdate.category != "local" && packageUpdate.matchedVersionUpdate.category != "gjlocal") + || permanentSuppressedUpdatePackageNameList?.contains(packageName) == true + || SPUtils.getBoolean(SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX + packageName + currentlyUpdatableVersion) + ) { + packageUpdate.mismatchedVersionUpdateList.clear() + } + + // 被忽略的更新不需要显示其它版本 + if (ignoredUpdatePackageNameList?.contains(packageName) == true) { + // 若我的版本与当前应用版本一致那么取其它版本的第一个来替换成我的版本 (避免出现忽略更新列表显示按钮为启动的问题) +// if (packageUpdate.matchedVersionUpdate.version == PackageUtils.getVersionNameByPackageName(packageName) +// && packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) { +// packageUpdate.matchedVersionUpdate = packageUpdate.mismatchedVersionUpdateList.first() +// } + packageUpdate.mismatchedVersionUpdateList.clear() + } + + // 根据状态分子列表 + // 1. 按钮为更新的我的游戏 + // 2. 按钮为启动的我的游戏 + // 3. 忽略更新的游戏 + when { + ignoredUpdatePackageNameList?.contains(packageName) == true -> { + ignoredPackageUpdateList.add(packageUpdate) + } + packageUpdate.matchedVersionUpdate.version == currentlyUpdatableVersion -> { + invalidPackageUpdateList.add(packageUpdate) + } + else -> { + validPackageUpdateList.add(packageUpdate) + } + } + } + + // 构建装饰好的页面列表 + if (validPackageUpdateList.isNotEmpty()) { + var totalMatchedVersionValidUpdateSize = 0L + val matchedVersionValidUpdateUrlList = arrayListOf() + val updateCount = validPackageUpdateList.size + for (packageUpdate in validPackageUpdateList) { + // 上一个样式为其它版本,添加白色分割线 + if (updatableDataItemList.lastOrNull()?.otherVersionUpdateHint != null + || updatableDataItemList.lastOrNull()?.otherVersionUpdate != null + ) { + updatableDataItemList.add(UpdatableDataItem(divider = WHITE)) + } + + if (packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) { + updatableDataItemList.add(UpdatableDataItem(normalUpdateWithArrow = packageUpdate.matchedVersionUpdate)) + updatableDataItemList.add( + UpdatableDataItem( + otherVersionUpdateHint = mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true, + miscPackageName = packageUpdate.matchedVersionUpdate.packageName, + miscVersion = packageUpdate.matchedVersionUpdate.currentVersion ?: "" + ) + ) + if (mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true) { + for (update in packageUpdate.mismatchedVersionUpdateList) { + updatableDataItemList.add(UpdatableDataItem(otherVersionUpdate = update)) + } + } + } else { + updatableDataItemList.add(UpdatableDataItem(normalUpdate = packageUpdate.matchedVersionUpdate)) + } + + mCachedMatchedVersionValidUpdateList.add(packageUpdate.matchedVersionUpdate) + + totalMatchedVersionValidUpdateSize += sizeStringToLong( + packageUpdate.matchedVersionUpdate.size ?: "0M" + ) + + matchedVersionValidUpdateUrlList.add(packageUpdate.matchedVersionUpdate.url ?: "") + } + + val text = getUpdateTextFromUrlList(matchedVersionValidUpdateUrlList) + + updatableDataItemList.add( + 0, + UpdatableDataItem( + header = "${updateCount}个游戏可更新,共${ + totalMatchedVersionValidUpdateSize.toProperReadableSize().replace(" ", "") + }", + miscShowUpdateAll = true, + miscUpdateText = text + ) + ) + updatableDataItemList.add(0, UpdatableDataItem(divider = GREY)) + } + + if (invalidPackageUpdateList.isNotEmpty()) { + if (updatableDataItemList.size != 0) { + updatableDataItemList.add(UpdatableDataItem(divider = WHITE)) + } + updatableDataItemList.add(UpdatableDataItem(divider = GREY)) + updatableDataItemList.add(UpdatableDataItem(header = "以下游戏有其他版本可以更新")) + for (packageUpdate in validPackageUpdateList) { + if (packageUpdate.mismatchedVersionUpdateList.isNotEmpty()) { + updatableDataItemList.add(UpdatableDataItem(normalUpdateWithArrow = packageUpdate.matchedVersionUpdate)) + updatableDataItemList.add( + UpdatableDataItem( + otherVersionUpdateHint = mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true + ) + ) + if (mOtherVersionExpandedMap[packageUpdate.matchedVersionUpdate.packageName] == true) { + for (update in packageUpdate.mismatchedVersionUpdateList) { + updatableDataItemList.add(UpdatableDataItem(otherVersionUpdate = update)) + } + } + } else { + updatableDataItemList.add(UpdatableDataItem(normalUpdate = packageUpdate.matchedVersionUpdate)) + } + } + } + + if (ignoredPackageUpdateList.isNotEmpty()) { + updatableDataItemList.add(UpdatableDataItem(divider = WHITE)) + updatableDataItemList.add(UpdatableDataItem(divider = GREY)) + updatableDataItemList.add(UpdatableDataItem(ignoredUpdateHeader = "${ignoredPackageUpdateList.size}个游戏已忽略更新")) + if (mShouldShowIgnoredUpdate) { + for (packageUpdate in ignoredPackageUpdateList) { + updatableDataItemList.add(UpdatableDataItem(ignoredUpdate = packageUpdate.matchedVersionUpdate)) + } + } + } + + return updatableDataItemList + } + + /** + * 根据所给的 url 列表返回头部右上角的文本内容 + */ + private fun getUpdateTextFromUrlList(matchedVersionValidUpdateUrlList: ArrayList) + : String { + var updateTaskCount = 0 + var updateCompleteTaskCount = 0 + for (url in matchedVersionValidUpdateUrlList) { + val downloadEntity = DownloadManager.getInstance(getApplication()) + .getDownloadEntityByUrl(url) + if (downloadEntity != null) { + updateTaskCount++ + if (downloadEntity.status == DownloadStatus.done) { + updateCompleteTaskCount++ + } + } + } + + return if (updateCompleteTaskCount == matchedVersionValidUpdateUrlList.size) { + "更新完成" + } else { + if (updateTaskCount == matchedVersionValidUpdateUrlList.size) { + "更新中" + } else { + "全部更新" + } + } + } + + /** + * 根据类型排序 + */ + private fun sortUpdatableListByType(list: List) { + tryCatchInRelease { + Collections.sort(list) { lhs, rhs -> + if ("光环助手" == rhs?.name) { + 1 + } else if (lhs?.name?.contains("光环助手") == true) { + -1 + } else if (lhs?.isPluggable != true && rhs?.isPluggable == true) { + -1 + } else if (lhs?.isPluggable == true && rhs?.isPluggable != true) { + 1 + } else { + 0 + } + } + } + } + + private fun getUpdateInfoByPackageName(packageName: String) { + val ghId: Any? = PackageUtils.getMetaData(getApplication(), packageName, "gh_id") + val retrofit = RetrofitManager.getInstance(getApplication()).api + + if (ghId == null) { + retrofit.loadGameDataByPackageName( + UrlFilterUtils.getFilterQuery("package", packageName) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response?>() { + override fun onResponse(response: List?) { + response?.let { + for (game in response) { + val simpleUpdatableList: List = + PackageUtils.getUpdateData(game) + + if (simpleUpdatableList.isNotEmpty()) { + mRawUpdatableList.addAll(simpleUpdatableList) + } + } + } + refreshList() + } + }) + } else { + retrofit.getGameUpdateById(ghId as String) + .map(ApkActiveUtils.filterMapper) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: GameEntity?) { + val simpleUpdatableList: List = + PackageUtils.getUpdateData(response) + + mRawUpdatableList.addAll(simpleUpdatableList) + + refreshList() + } + }) + } + + mSpecialPackageName = "" + } + + fun toggleIgnoredUpdateVisibility() { + mShouldShowIgnoredUpdate = !mShouldShowIgnoredUpdate + setUpdatableList(mRawUpdatableList) + } + + fun toggleOtherVersionVisibility(packageName: String) { + mOtherVersionExpandedMap[packageName] = !(mOtherVersionExpandedMap[packageName] ?: false) + setUpdatableList(mRawUpdatableList) + } + + fun suppressUpdate( + packageName: String, + currentUpdatableVersion: String, + suppressPermanently: Boolean + ) { + if (suppressPermanently) { + mSuppressUpdateDao.add(packageName) + } else { + SPUtils.setBoolean( + SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX + packageName + currentUpdatableVersion, + true + ) + } + setUpdatableList(mRawUpdatableList) + } + + fun ignoreUpdate(packageName: String) { + mIgnoredUpdateDao.add(packageName) + setUpdatableList(mRawUpdatableList) + } + + fun undoIgnoredUpdate(packageName: String) { + mIgnoredUpdateDao.delete(packageName) + setUpdatableList(mRawUpdatableList) + } + + fun isIgnoredUpdateExpanded(): Boolean { + return mShouldShowIgnoredUpdate + } + + /** + * 全部更新 (版本匹配的我的版本) + */ + fun updateAllMatchedVersionValidUpdate() { + for (update in mCachedMatchedVersionValidUpdateList) { + update(update, false) + } + } + + /** + * 更新列表状态 + */ + fun refreshList() { + setUpdatableList(mRawUpdatableList) + } + + /** + * 执行单个更新 + * @param update 更新内容 + * @param isSubscribe 是否延迟到连上 WIFI 才下载 + */ + fun update(update: GameUpdateEntity, isSubscribe: Boolean) { + val downloadType: ExposureUtils.DownloadType + val downloadId = createDownloadId(update.name) + + val downloadEntity = DownloadEntity() + downloadEntity.gameId = update.id + downloadEntity.url = update.url + downloadEntity.name = update.name + downloadEntity.path = getDownloadPathWithId(downloadId, update.format) + downloadEntity.eTag = update.etag + downloadEntity.icon = update.icon + downloadEntity.platform = update.platform + downloadEntity.packageName = update.packageName + downloadEntity.versionName = update.version + downloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, update.rawIcon) + downloadEntity.addMetaExtra(Constants.GAME_ICON_SUBSCRIPT, update.iconSubscript) + downloadEntity.addMetaExtra(Constants.DOWNLOAD_ID, downloadId) + + val platform = PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform) + if ("官方版" != platform) { + downloadEntity.isPlugin = true + } + if (update.isPluggable) { + downloadEntity.isPluggable = true + } else { + downloadEntity.isUpdate = true + } + + // 确定下载类型 + downloadType = when { + downloadEntity.isPluggable -> ExposureUtils.DownloadType.PLUGIN_DOWNLOAD + downloadEntity.isPlugin -> ExposureUtils.DownloadType.PLUGIN_UPDATE + else -> ExposureUtils.DownloadType.UPDATE + } + + val gameEntity = GameEntity(update.id, update.name) + gameEntity.gameVersion = update.version ?: "" + + val event = logADownloadExposureEvent( + gameEntity, + update.platform, + update.exposureEvent, + downloadType + ) + + downloadEntity.exposureTrace = toJson(event) + downloadEntity.entrance = "$entrance+(下载管理:游戏更新)" + downloadEntity.location = "游戏更新:列表" + if (update.name?.contains("光环助手") == true) { + DownloadManager.getInstance(getApplication()).pauseAll() + } + if (isSubscribe) { + DownloadManager.getInstance(getApplication()).subscribe(downloadEntity) + } else { + insertGameEntity(update) + DownloadManager.getInstance(getApplication()).add(downloadEntity) + } + + // 收集下载数据 + DataCollectionUtils.uploadDownload(getApplication(), downloadEntity, "开始") + } + + private fun sizeStringToLong(sizeString: String): Long { + val lastM = sizeString.lastIndexOf("M") + if (lastM != -1) { + return (sizeString.substring(0, lastM).toFloat() * 1024 * 1024).toLong() + } + return 0L + } + + companion object { + const val SP_TEMPORARY_SUPPRESS_UPDATE_PREFIX = "temporary_suppressed_update_prefix_" + + const val WHITE = "white" + const val GREY = "grey" + const val BLUE = "blue" + } + + /** + * - 分割线(包括灰色,白色和浅蓝色) + * - 信息头(包括更新信息和普通文本) + * - 忽略更新信息头 + * - 普通游戏 + * - 含小箭头的普通游戏 + * - 其它版本游戏 + * - 其它版本游戏提示(包括展开和收起状态) + * + * - 其它信息(包名) + */ + data class UpdatableDataItem( + var divider: String? = null, + var header: String? = null, + var ignoredUpdateHeader: String? = null, + var normalUpdate: GameUpdateEntity? = null, + var normalUpdateWithArrow: GameUpdateEntity? = null, + var ignoredUpdate: GameUpdateEntity? = null, + var otherVersionUpdate: GameUpdateEntity? = null, + var otherVersionUpdateHint: Boolean? = null, + + // 其它信息,是上面单独 item 信息的补充 + var miscPackageName: String = "", // 包名 + var miscVersion: String = "", // 版本 + var miscShowUpdateAll: Boolean = false, // 显示更新全部按钮 + var miscUpdateText: String = "" // 更新按钮的文案 (有`全部更新`,`更新中`,`已更新`三种) + ) + + private data class PackageUpdate( + var matchedVersionUpdate: GameUpdateEntity, // AKA 我的版本 + var mismatchedVersionUpdateList: ArrayList + ) + + private class SuppressUpdateDao : BaseSimpleDao() { + override fun getSPKey(): String = "suppressed_update" + } + + private class IgnoredUpdateDao : BaseSimpleDao() { + override fun getSPKey(): String = "ignored_update" + } + + class Factory(private var mSpecialPackageName: String, private var mEntrance: String) : + ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return UpdatableGameViewModel( + HaloApp.getInstance().application, + mSpecialPackageName, + mEntrance + ) as T + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt index 734ae474de..fa96d3bf88 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt @@ -33,6 +33,10 @@ data class ApkEntity(@SerializedName("package") @SerializedName("download_instruction") val downloadInstruction: String = "", var recommend: Recommend? = null, + @SerializedName("sign") + var signature: String? = "", + @SerializedName("version_code") + var versionCode: Int? = 0, // 以下是历史版本用的字段,其它地方可能会没有 @SerializedName("update_time") diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt b/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt index eee109902c..ac98d2f447 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt @@ -11,7 +11,7 @@ data class GameInstall( var name: String? = "", var icon: String? = "", var iconSubScript: String? = "", - var isSignature: Boolean = false, + var isSignByGh: Boolean = false, var installTime: Long = 0, var version: String = "", var tag: Any? = null) { @@ -20,13 +20,13 @@ data class GameInstall( fun transformGameInstall(game: GameEntity, installedPkgName: String): GameInstall { val gameInstall = GameInstall() val application = HaloApp.getInstance().application - gameInstall.isSignature = PackageUtils.isSignedByGh(application, installedPkgName) + gameInstall.isSignByGh = PackageUtils.isSignedByGh(application, installedPkgName) gameInstall.installTime = PackageUtils.getInstalledTime(application, installedPkgName) gameInstall.id = game.id gameInstall.name = game.name gameInstall.icon = game.rawIcon ?: game.icon gameInstall.iconSubScript = game.iconSubscript - gameInstall.version = PackageUtils.getVersionByPackage(installedPkgName) + gameInstall.version = PackageUtils.getVersionNameByPackageName(installedPkgName) gameInstall.packageName = installedPkgName return gameInstall } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt index f18ca384a8..cd5d2bdfb0 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt @@ -17,10 +17,12 @@ data class GameUpdateEntity( var packageName: String = "", var size: String? = null, var version: String? = null, + var currentVersion: String? = null, @SerializedName("gh_version") var ghVersion: String? = null, var url: String? = null, var platform: String? = null, + var readablePlatform: String? = "", var isPluggable: Boolean = false, var etag: String? = null, var brief: String? = null, @@ -36,7 +38,9 @@ data class GameUpdateEntity( @SerializedName("p_button_add_word") var pluginDesc: String = "", //插件功能描述 var pluggableCollection: GameCollectionEntity? = null, // 插件化包所在的合集 - var format: String = "" + var format: String = "", + var signature: String? = "", + var category: String? = "" ) { fun isShowPlugin(location: PluginLocation): Boolean { @@ -61,6 +65,7 @@ data class GameUpdateEntity( gameEntity.indexPlugin = indexPlugin gameEntity.pluginDesc = pluginDesc gameEntity.pluggableCollection = pluggableCollection + gameEntity.category = category val apkEntity = ApkEntity() apkEntity.url = url @@ -72,6 +77,7 @@ data class GameUpdateEntity( apkEntity.etag = etag apkEntity.plugin = plugin apkEntity.format = format + apkEntity.signature = signature val apk = ArrayList() apk.add(apkEntity) diff --git a/app/src/main/java/com/gh/gamecenter/game/upload/GameUploadFragment.kt b/app/src/main/java/com/gh/gamecenter/game/upload/GameUploadFragment.kt index 134e36cfb7..82d60ece1e 100644 --- a/app/src/main/java/com/gh/gamecenter/game/upload/GameUploadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/upload/GameUploadFragment.kt @@ -30,7 +30,6 @@ import com.gh.gamecenter.suggest.SuggestSelectGameAdapter import com.halo.assistant.HaloApp import com.lightgame.utils.Util_System_Keyboard import com.lightgame.utils.Utils -import com.zhihu.matisse.internal.utils.PathUtils import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.fragment_game_upload.* import okhttp3.MediaType @@ -456,7 +455,7 @@ class GameUploadFragment : NormalFragment() { } else if (requestCode == CHOOSE_LOCAL_APK) { val packageName = data.getStringExtra(EntranceUtils.KEY_PACKAGENAME) ?: "" val gamePath = data.getStringExtra(EntranceUtils.KEY_PATH) ?: "" - val version = PackageUtils.getVersionByPackage(packageName) + val version = PackageUtils.getVersionNameByPackageName(packageName) val length = File(gamePath).length() if (length > 5 * 1024 * 1024 * 1024) { ToastUtils.showToast(getString(R.string.apk_max_size_hint, 5)) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index db3d49a4b9..719bd221ba 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -699,7 +699,7 @@ class GameDetailFragment : NormalFragment() { private fun retrieveAvailableDialog(detailDialogs: ArrayList): GameEntity.Dialog? { for (dialog in detailDialogs) { - val versionName = PackageUtils.getVersionName() + val versionName = PackageUtils.getGhVersionName() val noticeVersions = dialog.rule.noticeVersions if (noticeVersions.isEmpty() || noticeVersions.contains(versionName)) { if (dialog.rule.models.isEmpty() || dialog.rule.models.contains(Build.MODEL)) { diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt index 04d7acda44..4e96ef1f34 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt @@ -182,7 +182,7 @@ class DescViewModel(application: Application, fun sendSuggestion() { val params = hashMapOf() params["from"] = "" - params["ghversion"] = PackageUtils.getVersionName() + params["ghversion"] = PackageUtils.getGhVersionName() params["channel"] = HaloApp.getInstance().channel params["type"] = Build.MODEL params["sdk"] = android.os.Build.VERSION.SDK_INT.toString() diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/history/HistoryApkListAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/history/HistoryApkListAdapter.kt index 465a50f7f4..593b731292 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/history/HistoryApkListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/history/HistoryApkListAdapter.kt @@ -176,7 +176,7 @@ class HistoryApkListAdapter(context: Context, } if (btnText == "安装" || btnText == "下载") { if (PackageUtils.isInstalled(mContext, apkEntity.packageName)) { - val installedVersion = PackageUtils.getVersionByPackage(apkEntity.packageName) + val installedVersion = PackageUtils.getVersionNameByPackageName(apkEntity.packageName) if (Version(installedVersion).isHigherThan(Version(apkEntity.version))) { return true } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt index ae9df24998..81b73d9ae2 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt @@ -362,14 +362,14 @@ class RatingEditActivity : ToolBarActivity(), KeyboardHeightObserver { val apk = mGame?.getApk() if (!apk.isNullOrEmpty()) apk[0].version else "" } else { - PackageUtils.getVersionByPackage(mInstallPackageName) + PackageUtils.getVersionNameByPackageName(mInstallPackageName) } val jsonObject = JSONObject() jsonObject.put("star", rating) jsonObject.put("content", content) jsonObject.put("show_device", mBinding.deviceBox.isChecked) jsonObject.put("device", DeviceUtils.getLoginDevice(this)) - jsonObject.put("gh_version", PackageUtils.getVersionName()) + jsonObject.put("gh_version", PackageUtils.getGhVersionName()) jsonObject.put("game_version", gameVersion) jsonObject.put("source", if (mFromAmway) "anliwall" else "game_detail") jsonObject.put("plugin_version", PackageUtils.getMetaData(this, mInstallPackageName, "gh_version")) diff --git a/app/src/main/java/com/gh/gamecenter/help/QaFeedbackViewModel.kt b/app/src/main/java/com/gh/gamecenter/help/QaFeedbackViewModel.kt index c148968b89..57106bb3e2 100644 --- a/app/src/main/java/com/gh/gamecenter/help/QaFeedbackViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/help/QaFeedbackViewModel.kt @@ -26,7 +26,7 @@ class QaFeedbackViewModel(application: Application, private val contentId: Strin fun qaSuggestions(message: String) { val map = hashMapOf() map["message"] = message - map["ghversion"] = PackageUtils.getVersionName() + map["ghversion"] = PackageUtils.getGhVersionName() map["channel"] = HaloApp.getInstance().channel map["type"] = Build.MODEL map["sdk"] = Build.VERSION.SDK_INT.toString() diff --git a/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java b/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java index 7d6bba7624..924aac3127 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java @@ -52,7 +52,7 @@ public class DataCollectionManager { * 实时上传 */ private void realTimeUpload(final String type, Map map) { - String version = PackageUtils.getVersionName(); + String version = PackageUtils.getGhVersionName(); String user = Installation.getUUID(mContext); String channel = HaloApp.getInstance().getChannel(); map.put("version", version); @@ -123,7 +123,7 @@ public class DataCollectionManager { return; } isUploading = true; - String version = PackageUtils.getVersionName(); + String version = PackageUtils.getGhVersionName(); String user = Installation.getUUID(mContext); String channel = HaloApp.getInstance().getChannel(); for (DataCollectionInfo dataCollectionInfo : list) { 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 08fb7bd43a..f914863adc 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java @@ -209,11 +209,11 @@ public class UpdateManager { loadingDialog = DialogUtils.showWaitDialog(mContext, "检查更新中..."); } String channel = HaloApp.getInstance().getChannel(); - RetrofitManager.getInstance(mContext).getApi().getUpdate(PackageUtils.getVersionName(), PackageUtils.getVersionCode(), channel) + RetrofitManager.getInstance(mContext).getApi().getUpdate(PackageUtils.getGhVersionName(), PackageUtils.getGhVersionCode(), channel) .map(appEntity -> { boolean isShowUpdateDialog = false; - if (appEntity.getVersionCode() > PackageUtils.getVersionCode()) { + if (appEntity.getVersionCode() > PackageUtils.getGhVersionCode()) { // 助手有更新 UpdateManager.this.appEntity = appEntity; @@ -554,11 +554,11 @@ public class UpdateManager { } private String getUpdateOnceOnlySpKey() { - return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getVersionCode(); + return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode(); } private String getUpdateOnceOnlySecondSpKey() { - return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getVersionCode(); + return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode(); } } diff --git a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt index 85d7f81493..f29357cbdc 100644 --- a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt @@ -1,7 +1,6 @@ package com.gh.gamecenter.packagehelper import android.annotation.SuppressLint -import android.preference.PreferenceManager import android.text.TextUtils import androidx.lifecycle.MutableLiveData import com.gh.common.exposure.meta.MetaUtil @@ -48,7 +47,6 @@ object PackageRepository { private val mApplication = HaloApp.getInstance().application private val mApi = RetrofitManager.getInstance(mApplication).api private val mSensitiveApi = RetrofitManager.getInstance(mApplication).sensitiveApi - private val mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApplication) private const val LAST_UPLOAD_APPLIST_TIME = "last_upload_applist_time" @@ -59,6 +57,7 @@ object PackageRepository { val gameInstalled = Collections.synchronizedList(ArrayList()) val gameUpdate = ArrayList() + val currentVersionList = ArrayList() // 当前版本游戏的列表 init { initData() @@ -92,12 +91,12 @@ object PackageRepository { * 把助手更新数据添加到下载管理(为了不改变原有的更新逻辑只能在这里多请求一次接口) */ private fun loadGhzsUpdate() { - mApi.getUpdate(PackageUtils.getVersionName(), PackageUtils.getVersionCode(), HaloApp.getInstance().channel) + mApi.getUpdate(PackageUtils.getGhVersionName(), PackageUtils.getGhVersionCode(), HaloApp.getInstance().channel) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { override fun onResponse(appEntity: AppEntity?) { - if (appEntity != null && appEntity.versionCode > PackageUtils.getVersionCode()) { + if (appEntity != null && appEntity.versionCode > PackageUtils.getGhVersionCode()) { val gameUpdateEntity = GameUpdateEntity() gameUpdateEntity.name = "光环助手V" + appEntity.version!! gameUpdateEntity.packageName = HaloApp.getInstance().application.packageName @@ -207,6 +206,7 @@ object PackageRepository { gameInstalled.add(GameInstall.transformGameInstall(game, pkgName)) val isCanPluggable = checkGamePlugin(game, pkgName) val isCanUpdate = checkGameUpdate(game) + addCurrentlyInstalledVersionIfValid(game) if (!isNotifyUpdate && isCanUpdate || isCanPluggable) { isNotifyUpdate = true } @@ -239,6 +239,36 @@ object PackageRepository { return false } + /** + * 添加当前版本的信息如果满足条件的话 + */ + private fun addCurrentlyInstalledVersionIfValid(game: GameEntity) { + for (apk in game.getApk()) { + if (apk.version == PackageUtils.getVersionNameByPackageName(apk.packageName) + && apk.signature == PackageUtils.getApkSignatureByPackageName(mApplication, apk.packageName)) { + currentVersionList.add(GameUpdateEntity().apply { + id = game.id + name = game.name + icon = game.icon + packageName = apk.packageName + size = apk.size + version = apk.version + ghVersion = apk.ghVersion + url = apk.url + platform = apk.getPlatform() + etag = apk.etag + brief = game.brief + tag = game.getTag() + tagStyle = game.tagStyle + indexPlugin = game.indexPlugin + pluginDesc = game.pluginDesc + format = apk.format + signature = apk.signature ?: "" + }) + } + } + } + /** * 检查游戏插件化 * @param game diff --git a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt index 83ed7fcc30..8458ff3cfe 100644 --- a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt @@ -6,7 +6,9 @@ import androidx.lifecycle.* import com.gh.gamecenter.entity.GameInstall import com.gh.gamecenter.entity.GameUpdateEntity import com.halo.assistant.HaloApp -import java.util.HashMap +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.set class PackageViewModel(application: Application, @@ -30,10 +32,30 @@ class PackageViewModel(application: Application, } } + /** + * 获取可更新列表的 LiveData(不过滤同包名) + */ fun getGameUpdateLiveData(): MutableLiveData> { return mRepository.gameUpdateLiveData } + /** + * 获取可更新列表的 LiveData(数据包括当前版本,供下载管理-更新显示我的版本用) + */ + fun getGameUpdateIncludeCurrentVersion(): MutableLiveData> { + val mediatorLiveData = MediatorLiveData>() + mediatorLiveData.addSource(mRepository.gameUpdateLiveData) { + val decoratedList = ArrayList(it) + for (currentVersion in mRepository.currentVersionList) { + decoratedList.add(currentVersion) + } + + mediatorLiveData.postValue(decoratedList) + } + + return mediatorLiveData + } + fun getGameInstalledLiveData(): MutableLiveData> { return mRepository.gameInstalledLiveData } diff --git a/app/src/main/java/com/gh/gamecenter/personal/NewPersonalFragment.kt b/app/src/main/java/com/gh/gamecenter/personal/NewPersonalFragment.kt index f2ee5dd861..1e43a6894b 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/NewPersonalFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/NewPersonalFragment.kt @@ -289,7 +289,7 @@ class NewPersonalFragment : BaseLazyFragment() { @SuppressLint("CheckResult", "SetTextI18n") private fun observeUIRelatedChanges() { mPersonalViewModel.appEntity.observe(this, { - if (it.versionCode > PackageUtils.getVersionCode()) { + if (it.versionCode > PackageUtils.getGhVersionCode()) { notifyItemChange("设置", FunctionalMessageType.NEW_VERSION) } else { notifyItemChange("设置", null) diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt index f05a557edd..999dfd69f3 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt @@ -254,7 +254,7 @@ class PersonalFragment : BaseLazyFragment() { @SuppressLint("CheckResult") private fun observeUIRelatedChanges() { mPersonalViewModel.appEntity.observe(this, { - if (it.versionCode > PackageUtils.getVersionCode()) { + if (it.versionCode > PackageUtils.getGhVersionCode()) { notifyItemChange("设置", FunctionalMessageType.NEW_VERSION) } else { notifyItemChange("设置", null) diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt index 98c0adcb74..5ea40bc4ce 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt @@ -123,8 +123,8 @@ class PersonalViewModel(application: Application) : AndroidViewModel(application RetrofitManager.getInstance(getApplication()) .api .getUpdate( - PackageUtils.getVersionName(), - PackageUtils.getVersionCode(), + PackageUtils.getGhVersionName(), + PackageUtils.getGhVersionCode(), HaloApp.getInstance().channel ) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/gh/gamecenter/qa/select/VotingViewModel.java b/app/src/main/java/com/gh/gamecenter/qa/select/VotingViewModel.java index 48f43f3251..03b61e18d2 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/select/VotingViewModel.java +++ b/app/src/main/java/com/gh/gamecenter/qa/select/VotingViewModel.java @@ -67,7 +67,7 @@ public class VotingViewModel extends ListViewModel params = new HashMap<>(); params.put("message", "问答社区推荐收录:" + entity.getGameName() + "(" + entity.getPackageName() + ", " + entity.getGameVersion() + ")"); params.put("from", ""); - params.put("ghversion", PackageUtils.getVersionName()); + params.put("ghversion", PackageUtils.getGhVersionName()); params.put("channel", HaloApp.getInstance().getChannel()); params.put("type", android.os.Build.MODEL); params.put("sdk", String.valueOf(android.os.Build.VERSION.SDK_INT)); diff --git a/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java index e021a88405..e1bee7eed5 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java @@ -36,7 +36,7 @@ public class InstallAndUninstallReceiver extends BroadcastReceiver { InstallUtils.getInstance(context).removeInstall(packageName); PackageHelper.refreshLocalPackageList(); - String versionName = PackageUtils.getVersionByPackage(packageName); + String versionName = PackageUtils.getVersionNameByPackageName(packageName); EBPackage installEb = new EBPackage("安装", packageName, versionName); if (PackageUtils.isAppOnForeground(context)) { PackageObserver.onPackageChanged(installEb); @@ -71,7 +71,7 @@ public class InstallAndUninstallReceiver extends BroadcastReceiver { if (packageName.equals(context.getPackageName())) { DataUtils.onEvent(context, "软件更新", "更新完成"); } - String versionName = PackageUtils.getVersionByPackage(packageName); + String versionName = PackageUtils.getVersionNameByPackageName(packageName); EBPackage updateEb = new EBPackage("替换", packageName, versionName); EventBus.getDefault().post(new EBPackage("替换", packageName, versionName)); diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java b/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java index b74c2b3854..10c25999b6 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/OkHttpCacheInterceptor.java @@ -115,7 +115,7 @@ class OkHttpCacheInterceptor implements Interceptor { request = request.newBuilder() .addHeader("JNFJ", MetaUtil.getBase64EncodedIMEI()) .addHeader("CHANNEL", HaloApp.getInstance().getChannel()) - .addHeader("VERSION", PackageUtils.getVersionName()) + .addHeader("VERSION", PackageUtils.getGhVersionName()) .addHeader("OAID", HaloApp.getInstance().getOAID()) .removeHeader("User-Agent") .addHeader("User-Agent", HaloApp.getInstance().getUserAgent()) diff --git a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt index dbcb9d2cc1..0ed3165238 100644 --- a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt @@ -119,7 +119,7 @@ class GameDownloadSettingFragment: NormalFragment() { @JvmStatic fun getTrafficDownloadHintKey(): String { - return PackageUtils.getVersionName() + "traffic_download_hint" + return PackageUtils.getGhVersionName() + "traffic_download_hint" } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt index ce8956577c..b37a10809f 100644 --- a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt @@ -113,7 +113,7 @@ class SimulatorGameListAdapter(context: Context, holder.simulatorBtn.run { if (PackageUtils.isInstalledFromAllPackage(context, simulator.apk?.packageName)) { - val versionFromInstalledApp = PackageUtils.getVersionByPackage(simulator.apk?.packageName) + val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator.apk?.packageName) val shouldShowUpdate = Version(simulator.apk?.version).isHigherThan(versionFromInstalledApp) text = if (shouldShowUpdate) "更新模拟器" else "卸载模拟器" setTextColor(if (shouldShowUpdate) R.color.theme.toColor() else R.color.text_999999.toColor()) diff --git a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorManagementAdapter.kt b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorManagementAdapter.kt index c93a53ece6..445a911867 100644 --- a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorManagementAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorManagementAdapter.kt @@ -49,7 +49,7 @@ class SimulatorManagementAdapter(context: Context, var shouldShowUpdate = false if (isInstalled) { - val versionFromInstalledApp = PackageUtils.getVersionByPackage(simulator.apk?.packageName) + val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator.apk?.packageName) shouldShowUpdate = Version(simulator.apk?.version).isHigherThan(versionFromInstalledApp) text = if (shouldShowUpdate) "更新" else "已安装" setTextColor(if (shouldShowUpdate) R.color.theme.toColor() else R.color.text_999999.toColor()) diff --git a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt index 246998ead6..e4533b65ed 100644 --- a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt @@ -886,7 +886,7 @@ class UploadVideoActivity : ToolBarActivity() { private fun getConfirmUpdateProtocolKey(): String { - return "ConfirmUpdateProtocolKey" + PackageUtils.getVersionName() + return "ConfirmUpdateProtocolKey" + PackageUtils.getGhVersionName() } companion object { diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index f1921491d1..147b15fb01 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -340,7 +340,7 @@ public class HaloApp extends MultiDexApplication { * 覆盖安装进入过首页的也当作是同意了 */ public static boolean isUserAcceptPrivacyPolicy(Context context) { - return !PreferenceManager.getDefaultSharedPreferences(context).getBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), true) + return !PreferenceManager.getDefaultSharedPreferences(context).getBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), true) || !SPUtils.getBooleanWithContext(context, Constants.SP_BRAND_NEW_USER, true) || SPUtils.getBooleanWithContext(context, Constants.SP_IS_USER_ACCEPTED_PRIVACY_STATEMENT, false); } diff --git a/app/src/main/java/com/halo/assistant/fragment/AboutFragment.java b/app/src/main/java/com/halo/assistant/fragment/AboutFragment.java index 4935a7b686..1fe55e43dc 100644 --- a/app/src/main/java/com/halo/assistant/fragment/AboutFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/AboutFragment.java @@ -74,7 +74,7 @@ public class AboutFragment extends NormalFragment { super.onCreate(savedInstanceState); setNavigationTitle(getString(R.string.title_about)); - mVersionName.setText("V" + PackageUtils.getVersionName()); + mVersionName.setText("V" + PackageUtils.getGhVersionName()); long serverTime = PreferenceManager.getDefaultSharedPreferences(requireContext()).getLong("server_time", 1587693163L); String year = TimeUtils.INSTANCE.getFormatTime(serverTime, "yyyy"); diff --git a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java index 14cef9877c..4dd876d6c7 100644 --- a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java @@ -151,9 +151,9 @@ public class SettingsFragment extends NormalFragment { } else { // Jenkins 打的包附带个打包时间在版本后面 if (BuildConfig.BUILD_TIME == 0) { - mVersionName.setText(("V" + PackageUtils.getVersionName())); + mVersionName.setText(("V" + PackageUtils.getGhVersionName())); } else { - mVersionName.setText(("V" + PackageUtils.getVersionName()) + " " + BuildConfig.BUILD_TIME); + mVersionName.setText(("V" + PackageUtils.getGhVersionName()) + " " + BuildConfig.BUILD_TIME); } } } diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.png deleted file mode 100644 index b51543329d..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.webp b/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.webp new file mode 100644 index 0000000000..8961bbefbc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_blue.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_blue.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_blue.png new file mode 100644 index 0000000000..84f088a92e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_blue.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_grey.webp b/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_grey.webp new file mode 100644 index 0000000000..b723810cd7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_down_grey.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_blue.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_blue.png new file mode 100644 index 0000000000..61894f2129 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_blue.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_grey.webp b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_grey.webp new file mode 100644 index 0000000000..a70a781717 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_grey.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_light_blue.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_light_blue.png new file mode 100644 index 0000000000..38460cf3d9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up_light_blue.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dot_more.png b/app/src/main/res/drawable-xxxhdpi/ic_dot_more.png new file mode 100644 index 0000000000..4b7321fd16 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dot_more.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_downloading_hint.webp b/app/src/main/res/drawable-xxxhdpi/ic_downloading_hint.webp new file mode 100644 index 0000000000..219a67df1a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_downloading_hint.webp differ diff --git a/app/src/main/res/layout/downloadmanager.xml b/app/src/main/res/layout/downloadmanager.xml index a629dd4adb..4513f1a2d1 100644 --- a/app/src/main/res/layout/downloadmanager.xml +++ b/app/src/main/res/layout/downloadmanager.xml @@ -1,5 +1,7 @@ @@ -9,6 +11,41 @@ android:layout_height="match_parent" android:background="@color/white" /> + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_updatable_game.xml b/app/src/main/res/layout/item_updatable_game.xml new file mode 100644 index 0000000000..ff27ce1813 --- /dev/null +++ b/app/src/main/res/layout/item_updatable_game.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_updatable_game_header.xml b/app/src/main/res/layout/item_updatable_game_header.xml new file mode 100644 index 0000000000..3252d3edb0 --- /dev/null +++ b/app/src/main/res/layout/item_updatable_game_header.xml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_updatable_other_game_hint.xml b/app/src/main/res/layout/item_updatable_other_game_hint.xml new file mode 100644 index 0000000000..0cea46207b --- /dev/null +++ b/app/src/main/res/layout/item_updatable_other_game_hint.xml @@ -0,0 +1,35 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_item_download_number.xml b/app/src/main/res/layout/tab_item_download_number.xml index 957959048e..0001d83a83 100644 --- a/app/src/main/res/layout/tab_item_download_number.xml +++ b/app/src/main/res/layout/tab_item_download_number.xml @@ -8,7 +8,7 @@ android:id="@+id/tab_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="8dp" + android:layout_marginStart="8dp" android:text="@string/download_game" android:textColor="@color/text_tabbar_style" android:textSize="14sp" /> @@ -17,7 +17,7 @@ android:id="@+id/tab_download_number" android:layout_width="6dp" android:layout_height="6dp" - android:layout_marginLeft="2dp" + android:layout_marginStart="2dp" android:layout_marginBottom="3dp" android:background="@drawable/oval_hint_red_bg" android:gravity="center" diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b2abcc72d7..4b54d5711a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -158,6 +158,7 @@ #74FFFF #60D5FF #EDF5FC + #F2F7FC #b2b2b2 #9a9a9a #3a3a3a @@ -240,6 +241,7 @@ #140B6D #16161A #28282E + #8798A8 #3796FF #213964 #06CEA8 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 724764f924..5b58cc8556 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -99,6 +99,7 @@ 免流量传送 游戏下载 游戏更新 + 已安装 已连接 无需操作,请让对方继续发送即可 完成