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 b1c181b2c4..8a95238913 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -5,7 +5,6 @@ import android.content.SharedPreferences; import android.os.Build; import android.preference.PreferenceManager; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.Nullable; @@ -22,6 +21,7 @@ import com.gh.gamecenter.common.utils.EnvHelper; import com.gh.gamecenter.core.utils.GsonUtils; import com.gh.gamecenter.core.utils.SPUtils; import com.gh.gamecenter.core.utils.UrlFilterUtils; +import com.gh.gamecenter.entity.AppEntity; import com.gh.gamecenter.entity.GameGuidePopupEntity; import com.gh.gamecenter.entity.NewApiSettingsEntity; import com.gh.gamecenter.entity.NewSettingsEntity; @@ -32,6 +32,7 @@ import com.gh.gamecenter.feature.entity.SettingsEntity; import com.gh.gamecenter.feature.entity.SimulatorEntity; import com.gh.gamecenter.feature.utils.ContentBlockedHelper; import com.gh.gamecenter.retrofit.RetrofitManager; +import com.gh.gamecenter.retrofit.service.VApiService; import com.gh.vspace.VHelper; import com.halo.assistant.HaloApp; import com.lightgame.utils.Utils; @@ -44,7 +45,10 @@ import java.io.IOException; import java.util.List; import java.util.Locale; +import io.reactivex.Single; +import io.reactivex.SingleSource; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; import okhttp3.ResponseBody; @@ -76,6 +80,8 @@ public class Config { private static SimulatorEntity mNewSimulatorEntity; private static VSetting mVSetting; private static VNewSetting mVNewSetting; + + private static AppEntity mNew32UpdateEntity; public static BehaviorSubject vNewSettingSubject = BehaviorSubject.create(); private static GameGuidePopupEntity mGameGuidePopupEntity; private static SharedPreferences mDefaultSharedPreferences; @@ -326,6 +332,11 @@ public class Config { return mVNewSetting; } + @Nullable + public static AppEntity getNew32UpdateEntity() { + return mNew32UpdateEntity; + } + /** * 请求网络数据,尝试刷新畅玩相关配置 */ @@ -348,16 +359,30 @@ public class Config { @SuppressLint("CheckResult") public static void getNewSetting() { - RetrofitManager.getInstance() - .getVApi().getNewSettings(BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new BiResponse() { + VApiService vApi = RetrofitManager.getInstance().getVApi(); + vApi.getNewSettings(BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT).flatMap(new Function>() { @Override - public void onSuccess(VNewSetting data) { + public SingleSource apply(VNewSetting data) throws Exception { mVNewSetting = data; vNewSettingSubject.onNext(mVNewSetting); SPUtils.setString(Constants.SP_V_NEW_SETTINGS, GsonUtils.toJson(data)); + if (data.getVa() != null && data.getVa().getArch32() != null) { + String versionNameByPackageName = PackageUtils.getVersionNameByPackageName(data.getVa().getArch32().getPackageName()); + return vApi.getNewPackageUpdate( + BuildConfig.VERSION_NAME, + versionNameByPackageName != null ? versionNameByPackageName : "", + HaloApp.getInstance().getChannel() + ); + } + return Single.error(new IllegalStateException("VNewSetting entity is not expected")); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new BiResponse() { + @Override + public void onSuccess(AppEntity data) { + mNew32UpdateEntity = data; } }); } diff --git a/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt index 637eb780e1..33d1722922 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt @@ -34,7 +34,9 @@ class AppEntity( */ var alert: String? = null, // 关联64位更新 - var relation: AppEntity? = null + var relation: AppEntity? = null, + @SerializedName("_id") + var id: String? = null ) : Parcelable { fun isAlertEveryTime() = alert == "EVERY_TIME_OPEN" diff --git a/app/src/main/java/com/gh/vspace/VHelper.kt b/app/src/main/java/com/gh/vspace/VHelper.kt index 6315922a21..9d34b7e0e4 100644 --- a/app/src/main/java/com/gh/vspace/VHelper.kt +++ b/app/src/main/java/com/gh/vspace/VHelper.kt @@ -84,7 +84,6 @@ object VHelper { private const val KEY_LAST_PLAYED_TIME = "last_played_time" private const val KEY_LAST_ALERT_64_UPDATE_URL = "last_alert_64_update_url" private const val KEY_LAST_ALERT_32_UPDATE_URL = "last_alert_32_update_url" - private const val KEY_LAST_ALERT_32_NEW_UPDATE_URL = "last_alert_32_new_update_url" private const val KEY_TOTAL_PLAYED_TIME = "total_played_time" private const val G_GMS_PACKAGE_NAME = "com.google.android.gms" @@ -114,7 +113,6 @@ object VHelper { private var m64UpdateEntity: AppEntity? = null private var m32UpdateEntity: AppEntity? = null - private var mNew32UpdateEntity: AppEntity? = null private var mIsInitialized = false // 是否已初始化 private var mIsServiceConnected = false // AIDL 服务是否成功连接,不可作为 AIDL 仍然可用的依据 @@ -270,12 +268,6 @@ object VHelper { getVSpaceUpdate(config.arch32, false) } - val newConfig = Config.getVNewSettingEntity()?.va - if (newConfig?.arch32 != null && mNew32UpdateEntity == null) { - // 检查畅玩助手 32 位组件(新)更新 - getVNewSpaceUpdate(newConfig.arch32.packageName) - } - runOnIoThread { refreshVGameSnapshot() } @@ -677,18 +669,24 @@ object VHelper { private fun newCwValidateVspaceBeforeAction(context: Context, gameEntity: GameEntity?, callback: () -> Unit) { val bit = gameEntity?.gameBit ?: "" if (bit == "32") { - val vaConfig = Config.getVNewSettingEntity()?.va - if (vaConfig == null) { + val vaArch32 = Config.getVNewSettingEntity()?.va?.arch32 + if (vaArch32 == null) { ToastUtils.toast("畅玩助手空间暂未上线") return } - val is32VSpaceInstalled = PackageUtils.isInstalledFromAllPackage(context, vaConfig.arch32?.packageName) + val new32UpdateEntity = Config.getNew32UpdateEntity() + val is32VSpaceInstalled = PackageUtils.isInstalledFromAllPackage(context, vaArch32.packageName) if (!is32VSpaceInstalled) { val appEntity = AppEntity() - appEntity.versionCode = vaConfig.arch32?.versionCode ?: 0 - appEntity.version = vaConfig.arch32?.versionName - appEntity.url = vaConfig.arch32?.url - + if (new32UpdateEntity != null && (new32UpdateEntity.versionCode > vaArch32.versionCode)) { + appEntity.versionCode = new32UpdateEntity.versionCode + appEntity.version = new32UpdateEntity.version + appEntity.url = new32UpdateEntity.url + } else { + appEntity.versionCode = vaArch32.versionCode + appEntity.version = vaArch32.versionName + appEntity.url = vaArch32.url + } VSpace32NewDialogFragment.showDownloadDialog( context, appEntity, @@ -697,38 +695,29 @@ object VHelper { ) return } - val contains32Update = newCw32ShouldShowUpdate( - mNew32UpdateEntity, - PackageUtils.getVersionCodeByPackageName(vaConfig.arch32?.packageName) - ) - if (contains32Update) { - val updateEntity = mNew32UpdateEntity - val dialogType = if (updateEntity!!.isAlertEveryTime()) "强制更新" else "提示更新" + + if(new32UpdateEntity != null && (PackageUtils.getVersionCodeByPackageName(vaArch32.packageName) < new32UpdateEntity.versionCode)) { + val dialogType = if (new32UpdateEntity.isForce) "强制更新" else "提示更新" NewFlatLogUtils.logHaloFunUpdateDialogShow( gameEntity?.id ?: "", gameEntity?.name ?: "", "32位" ) - SPUtils.setString( - KEY_LAST_ALERT_32_NEW_UPDATE_URL, - updateEntity.url + updateEntity.alert - ) DialogHelper.showDialog( context = context, title = "服务工具更新提示", - content = updateEntity.content.toString(), + content = new32UpdateEntity.content.toString(), cancelText = "立即更新", - confirmText = "继续游戏", + confirmText = if (new32UpdateEntity.isForce) "取消" else "继续游戏", cancelClickCallback = { NewFlatLogUtils.logHaloFunUpdateDialogClick( dialogType, "立即更新", "32位" ) - VSpaceDialogFragment.showDownloadDialog( + VSpaceUpdate32NewDialogFragment.showDownloadDialog( context, - null, - updateEntity, + new32UpdateEntity, gameEntity, autoDownload = true, isUpdate = true @@ -736,11 +725,13 @@ object VHelper { }, confirmClickCallback = { NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "继续游戏", "") - callback.invoke() + if(!new32UpdateEntity.isForce) { + callback.invoke() + } }, extraConfig = DialogHelper.Config(centerTitle = true), uiModificationCallback = { - if (updateEntity.isAlertEveryTime()) { + if (new32UpdateEntity.isAlertEveryTime()) { it.confirmTv.visibility = View.GONE it.cancelTv.setTextColor(R.color.theme_font.toColor(context)) } @@ -1750,24 +1741,6 @@ object VHelper { }) } - @SuppressLint("CheckResult") - private fun getVNewSpaceUpdate(packageName: String) { - val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName) ?: "" - RetrofitManager.getInstance() - .vApi - .getNewPackageUpdate( - version = PackageUtils.getGhVersionName(), - vaVersion = installedVersionName, - channel = HaloApp.getInstance().channel - ) - .subscribeOn(Schedulers.io()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: AppEntity) { - mNew32UpdateEntity = data - } - }) - } - /** * 通过 PackageRepository 检查更新 */ @@ -1811,27 +1784,6 @@ object VHelper { return false } - private fun newCw32ShouldShowUpdate( - updateEntity: AppEntity?, - installedSpaceVersionCode: Int, - ): Boolean { - if (updateEntity == null) return false - - val hasNewerVersion = installedSpaceVersionCode < updateEntity.versionCode - - if (!hasNewerVersion) return false - - if (updateEntity.isAlertEveryTime()) return true - - val lastAlertUpdateUrl = updateEntity.url + updateEntity.alert - if (updateEntity.isAlertOnceADay() - && SPUtils.getString(KEY_LAST_ALERT_32_NEW_UPDATE_URL) != lastAlertUpdateUrl - ) { - return true - } - return false - } - /** * 打开畅玩广场(版块) */ diff --git a/app/src/main/java/com/gh/vspace/VSpace32NewDialogFragment.kt b/app/src/main/java/com/gh/vspace/VSpace32NewDialogFragment.kt index c03ff2ffea..21fa9cfda2 100644 --- a/app/src/main/java/com/gh/vspace/VSpace32NewDialogFragment.kt +++ b/app/src/main/java/com/gh/vspace/VSpace32NewDialogFragment.kt @@ -119,7 +119,7 @@ class VSpace32NewDialogFragment : BaseDraggableDialogFragment() { } else { val downloadEntity = DownloadEntity() - val name = "畅玩助手V" + mAppEntity?.version + "(32位)" + val name = "畅玩游戏助手V" + mAppEntity?.version val downloadId = PackageInstaller.createDownloadId(name) downloadEntity.url = mDownloadUrl diff --git a/app/src/main/java/com/gh/vspace/VSpaceUpdate32NewDialogFragment.kt b/app/src/main/java/com/gh/vspace/VSpaceUpdate32NewDialogFragment.kt new file mode 100644 index 0000000000..556afb80ea --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VSpaceUpdate32NewDialogFragment.kt @@ -0,0 +1,303 @@ +package com.gh.vspace + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import com.gh.common.exposure.ExposureUtils +import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD +import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.PackageInstaller +import com.gh.common.util.PackageUtils +import com.gh.gamecenter.feature.view.DownloadButton +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.DialogVspaceUpdate32NewBinding +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.halo.assistant.HaloApp +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadConfig +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus.* +import com.lightgame.utils.AppManager +import java.io.File + +class VSpaceUpdate32NewDialogFragment : BaseDialogFragment() { + + private var mAppEntity: AppEntity? = null + private var mGameId: String = "" + private var mGameName: String = "" + private val mDownloadUrl by lazy { mAppEntity?.url ?: "" } + private val mBinding by lazy { DialogVspaceUpdate32NewBinding.inflate(layoutInflater) } + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.url == mDownloadUrl && isAdded) { + updateDownloadButton(downloadEntity) + } + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + mAppEntity = arguments?.get(KEY_APP_ENTITY_32) as AppEntity + mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: "" + mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: "" + } + + @SuppressLint("ClickableViewAccessibility") + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return mBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val mViewModel = viewModelProvider() + mViewModel.packageLiveData.observe(this) { + if (it.packageName == VHelper.VSPACE_32BIT_NEW_PACKAGENAME) { + dismissAllowingStateLoss() + } + } + + val downloadSnapshot = DownloadManager.getInstance() + .getDownloadEntitySnapshotByPackageName(VHelper.VSPACE_32BIT_NEW_PACKAGENAME) + mBinding.downloadBtn.text = if (downloadSnapshot?.status == done) "安装" else "下载" + mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL + mBinding.contentTv.text = "《$mGameName》需安装完整的畅玩服务组件,安装后即可给您带来急速的畅玩体验~" + + mBinding.downloadBtn.setOnClickListener { + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName) + if (downloadSnapshot?.status == done) { + if (!File(downloadSnapshot.path).exists()) { + ToastUtils.toast("畅玩组件已损坏,即将重新下载") + DownloadManager.getInstance().cancel(downloadSnapshot.url) + DownloadManager.getInstance().add(downloadSnapshot) + } else { + PackageInstaller.install(requireContext(), downloadSnapshot) + } + } else { + val downloadEntity = DownloadEntity() + + val name = "畅玩游戏助手V" + mAppEntity?.version + val downloadId = PackageInstaller.createDownloadId(name) + + downloadEntity.url = mDownloadUrl + downloadEntity.name = name + downloadEntity.platform = "官方版" + downloadEntity.gameId = Constants.HALO_FUN_NEW_32_GAME_ID + downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk") + downloadEntity.packageName = VHelper.VSPACE_32BIT_NEW_PACKAGENAME + downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200") + + val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name) + gameEntity.gameVersion = mAppEntity?.version ?: "" + + // 确定下载类型 + val downloadType = + if (arguments?.getBoolean(VSpaceDialogFragment.KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD + + downloadEntity.exposureTrace = ExposureUtils.logADownloadExposureEvent( + gameEntity, + downloadEntity.platform, + null, + downloadType + ).toJson() + + AppExecutor.uiExecutor.executeWithDelay({ + DownloadManager.getInstance().cancel(mDownloadUrl) + DownloadManager.getInstance().add(downloadEntity) + }, 200) + + DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始") + } + } + + } + + override fun onStart() { + super.onStart() + DownloadManager.getInstance().addObserver(mDataWatcher) + + // 上面监听安装包名变化的 LiveData 监听有可能被冲掉了 + // 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题 + if (PackageUtils.isInstalledFromAllPackage(requireContext(), VHelper.VSPACE_32BIT_NEW_PACKAGENAME) + && mAppEntity?.version == PackageUtils.getVersionNameByPackageName(VHelper.VSPACE_32BIT_NEW_PACKAGENAME) + ) { + dismissAllowingStateLoss() + } + } + + override fun onStop() { + super.onStop() + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + + private fun updateDownloadButton(downloadEntity: DownloadEntity) { + val downloadBtn = mBinding.downloadBtn + downloadBtn.progress = (downloadEntity.progress * 10).toInt() + when (downloadEntity.status) { + downloading -> { + downloadBtn.setText(R.string.pause) + downloadBtn.progress = (downloadEntity.percent * 10).toInt() + downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().pause(mDownloadUrl) + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName) + } + } + + pause -> { + downloadBtn.setText(R.string.resume) + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, true) + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName) + } + } + + overflow, + timeout, + neterror, + diskisfull, + waiting, + subscribe -> { + downloadBtn.setText(R.string.waiting) + downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName) + } + } + + done -> { + downloadBtn.setText(R.string.install) + downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL + + downloadBtn.setOnClickListener { + if (!File(downloadEntity.path).exists()) { + ToastUtils.toast("畅玩组件已损坏,即将重新下载") + DownloadManager.getInstance().cancel(downloadEntity.url) + DownloadManager.getInstance().add(downloadEntity) + } else { + PackageInstaller.install(requireContext(), downloadEntity) + NewFlatLogUtils.logHaloFun32DialogEvent( + "halo_fun_32_update_tip_dialog_click", + mGameId, + mGameName + ) + } + } + } + + cancel, + hijack, + notfound, + uncertificated, + unqualified -> { + downloadBtn.text = "下载" + downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, true) + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName) + } + } + + else -> { + // do nothing + } + } + } + + companion object { + const val KEY_APP_ENTITY_32 = "app_entity_32" + const val KEY_AUTO_DOWNLOAD = "auto_download" + const val KEY_IS_UPDATE = "is_update" + + @JvmStatic + fun showDownloadDialog( + context: Context?, + appEntity32: AppEntity, + autoDownload: Boolean = false, + isUpdate: Boolean = false, + gameId: String = "", + gameName: String = "" + ) { + val fragmentActivity: FragmentActivity = if (context is FragmentActivity) { + context + } else { + val currentActivity = AppManager.getInstance().currentActivity() + if (currentActivity is FragmentActivity) { + currentActivity + } else { + throw IllegalStateException("current activity context must be FragmentActivity") + } + } + + // 防止重复弹出 + if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return + + NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_show", gameId, gameName) + + val downloadDialog = VSpaceUpdate32NewDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(KEY_APP_ENTITY_32, appEntity32) + putString(EntranceConsts.KEY_GAME_ID, gameId) + putString(EntranceConsts.KEY_GAME_NAME, gameName) + putBoolean(KEY_AUTO_DOWNLOAD, autoDownload) + putBoolean(KEY_IS_UPDATE, isUpdate) + } + } + downloadDialog.show( + fragmentActivity.supportFragmentManager, + VSpaceUpdate32NewDialogFragment::class.java.name + ) + } + + @JvmStatic + fun showDownloadDialog( + context: Context?, + appEntity32: AppEntity, + gameEntity: GameEntity?, + autoDownload: Boolean = false, + isUpdate: Boolean = false + ) { + showDownloadDialog( + context, + appEntity32, + autoDownload, + isUpdate, + gameEntity?.id ?: "", + gameEntity?.name ?: "" + ) + } + + private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean { + val fragments: List = fragmentActivity.supportFragmentManager.fragments + fragments.forEach { fragment -> + if (fragment is VSpaceUpdate32NewDialogFragment) return true + } + return false + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_vspace_update_32_new.xml b/app/src/main/res/layout/dialog_vspace_update_32_new.xml new file mode 100644 index 0000000000..ea1466dae2 --- /dev/null +++ b/app/src/main/res/layout/dialog_vspace_update_32_new.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vasdk b/vasdk index b5c876432e..8b9a01af08 160000 --- a/vasdk +++ b/vasdk @@ -1 +1 @@ -Subproject commit b5c876432e675298379bbba7c039ef58dab2b23c +Subproject commit 8b9a01af0845d59e63b809a054fe2f2b50cac256