diff --git a/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt b/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt index c65b8a7e48..4d99d6c002 100644 --- a/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt +++ b/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt @@ -29,6 +29,15 @@ class PrivacyPolicyDialogFragment : BaseDialogFragment() { private var mCallBack: ((isSuccess: Boolean) -> Unit)? = null private val mBinding by lazy { DialogPrivacyProtocolBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt index 14005467f3..eabef3281a 100644 --- a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt +++ b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt @@ -24,6 +24,15 @@ class ReserveDialog : BaseDialogFragment() { private lateinit var mReserveList: List private var mDismissListener: (() -> Unit)? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val binding: DialogReserveBinding = DialogReserveBinding.inflate(layoutInflater, null, false) diff --git a/app/src/main/java/com/gh/common/prioritychain/GlobalPriortiyChain.kt b/app/src/main/java/com/gh/common/prioritychain/GlobalPriortiyChain.kt new file mode 100644 index 0000000000..6453426368 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/GlobalPriortiyChain.kt @@ -0,0 +1,210 @@ +package com.gh.common.prioritychain + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.Application.ActivityLifecycleCallbacks +import android.os.Bundle +import android.preference.PreferenceManager +import androidx.fragment.app.FragmentActivity +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.PackageUtils +import com.gh.gamecenter.SplashScreenActivity +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.SimpleGameEntity +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.DialogEntity +import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.login.user.UserManager +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers + +/** + * 全局的 APP 优先级弹窗链管理器 + * + * 弹窗的优先级为 + * 启动跳转(-101) > 更新弹窗(-100) > 隐私政策弹窗(-99) > 消息通知权限弹窗(0) > 预约弹窗(1) > 启动弹窗(4) + * + */ +object GlobalPriorityChainHelper { + + private val mApi = RetrofitManager.getInstance().api + private val mPriorityChain: PriorityChain by lazy { PriorityChain() } + + /** + * 当前 activity 是否使用于应用全局弹窗的弹出 + * 排除启动页和其它非 FragmentActivity + */ + fun isThisActivityValid(activity: Activity?): Boolean { + return activity is FragmentActivity + && !activity.isFinishing + && activity !is SplashScreenActivity + } + + /** + * 启动优先级弹窗管理链的执行 + */ + fun start() { + if (!mPriorityChain.isHandlerQueueEmpty()) return + + val launchRedirectHandler = LaunchRedirectHandler(-101) + val updateDialogHandler = UpdateDialogHandler(-100) + val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99) + val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0) + val reserveDialogHandler = ReserveDialogHandler(1) + val welcomeDialogHandler = WelcomeDialogHandler(4) + + mPriorityChain.addHandler(launchRedirectHandler) + mPriorityChain.addHandler(updateDialogHandler) + mPriorityChain.addHandler(privacyPolicyDialogHandler) + mPriorityChain.addHandler(welcomeDialogHandler) + mPriorityChain.addHandler(reserveDialogHandler) + mPriorityChain.addHandler(notificationPermissionDialogHandler) + + updateDialogHandler.doPreProcess() + requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler) + requestReserveDialogData(reserveDialogHandler) + + mPriorityChain.start() + + observeLifecycle() + } + + private fun observeLifecycle() { + HaloApp.getInstance().registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + // do nothing + } + + override fun onActivityStarted(activity: Activity) { + // do nothing + } + + override fun onActivityResumed(activity: Activity) { + // 优先级弹窗管理链为空时取消注册,避免无用调用 + if (mPriorityChain.isHandlerQueueEmpty()) { + HaloApp.getInstance().unregisterActivityLifecycleCallbacks(this) + } else { + resume() + } + } + + override fun onActivityPaused(activity: Activity) { + // do nothing + } + + override fun onActivityStopped(activity: Activity) { + // do nothing + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + // do nothing + } + + override fun onActivityDestroyed(activity: Activity) { + // do nothing + } + }) + } + + /** + * 恢复链的处理 + */ + fun resume() { + mPriorityChain.resume() + } + + /** + * 全局的 APP 优先级弹窗管理链是否为空 + */ + fun isGlobalHandlerQueueEmpty(): Boolean { + return mPriorityChain.isHandlerQueueEmpty() + } + + /** + * 请求首页启动弹窗相关的数据并执行相关 handler 的 preProcess + */ + @SuppressLint("CheckResult") + private fun requestOpeningDialogData( + welcomeDialogHandler: WelcomeDialogHandler, + privacyPolicyDialogHandler: PrivacyPolicyDialogHandler + ) { + val sp = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance()) + + val lastId = SPUtils.getString(sp, Constants.SP_LAST_OPENING_ID, "") + val lastTime = SPUtils.getLong(sp, Constants.SP_LAST_OPENING_TIME, 0) + val openType = if (HaloApp.getInstance().isNewForThisVersion) "first" else "not_first_time" + + mApi.getOpeningDialog(HaloApp.getInstance().channel, lastId, lastTime, openType) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: DialogEntity) { + var welcomeDialogEntity: WelcomeDialogEntity? = null + var privacyPolicyDialogEntity: DialogEntity.PrivacyPolicyEntity? = null + + // 全新安装忽略隐私弹窗 + if (data.privacyPolicyDialog != null) { + val id = data.privacyPolicyDialog.id + val lastAcceptedId = SPUtils.getString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, "") + if (HaloApp.getInstance().isBrandNewInstall) { + SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, id) + } else { + if (id != lastAcceptedId) { + privacyPolicyDialogEntity = data.privacyPolicyDialog + } + } + } + + privacyPolicyDialogHandler.doPreProcess(privacyPolicyDialogEntity) + + // 类型为游戏时判断是否本地已安装该游戏,已安装不弹弹窗 + if (data.welcomeDialog != null) { + welcomeDialogEntity = data.welcomeDialog + + if (data.welcomeDialog.type == "game") { + if (data.welcomeDialog.packages != null) { + for (packageName in data.welcomeDialog.packages!!) { + if (PackageUtils.isInstalled(HaloApp.getInstance(), packageName)) { + welcomeDialogEntity = null + break + } + } + } + } + } + + welcomeDialogHandler.doPreProcess(welcomeDialogEntity) + } + + override fun onFailure(exception: Exception) { + privacyPolicyDialogHandler.doPreProcess(null) + } + }) + } + + /** + * 请求预约弹窗相关的数据 + */ + @SuppressLint("CheckResult") + private fun requestReserveDialogData(reserveDialogHandler: ReserveDialogHandler) { + if (CheckLoginUtils.isLogin()) { + mApi.getReserveDialog(UserManager.getInstance().userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + reserveDialogHandler.doPreProcess(data) + } + + override fun onFailure(exception: Exception) { + reserveDialogHandler.doPreProcess(null) + } + }) + } else { + reserveDialogHandler.doPreProcess(null) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt new file mode 100644 index 0000000000..0743be84f3 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt @@ -0,0 +1,16 @@ +package com.gh.common.prioritychain + +import com.gh.gamecenter.core.runOnUiThread + +/** + * 初次启动跳转 + */ +class LaunchRedirectHandler(private val mPriority: Int): PriorityChainHandler(mPriority) { + + override fun onProcess() { + runOnUiThread { + processNext() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt b/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt index 7588c2eea7..c45617c8e7 100644 --- a/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt +++ b/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt @@ -7,16 +7,32 @@ class PriorityChain { private val mHandlerQueue: Queue = PriorityBlockingQueue() + /** + * 添加 handler 到队列中 + */ fun addHandler(handler: PriorityChainHandler) { mHandlerQueue.add(handler.also { it.setPriorityChain(this) }) } + /** + * 启动队列中的 handler + */ fun start() { - mHandlerQueue.poll()?.process(mHandlerQueue) + mHandlerQueue.peek()?.process(mHandlerQueue) } + /** + * 恢复队列中的 handler + */ + fun resume() { + mHandlerQueue.peek()?.onProcess() + } + + /** + * 队列是否为空 + */ fun isHandlerQueueEmpty() = mHandlerQueue.isEmpty() } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt b/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt index 47dc1b5c20..64f345f299 100644 --- a/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt @@ -52,7 +52,8 @@ abstract class PriorityChainHandler(private val mPriority: Int) : Comparable { - PrivacyPolicyDialogFragment.show(mActivity!!, mPrivacyPolicyEntity) { _: Boolean? -> + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { + PrivacyPolicyDialogFragment.show(currentActivity as FragmentActivity, mPrivacyPolicyEntity) { _: Boolean? -> + processNext() + } + } + + STATUS_INVALID -> { processNext() } } - STATUS_INVALID -> { - processNext() - } } } diff --git a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt index cb385d42b6..1596c12f51 100644 --- a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt @@ -1,20 +1,18 @@ package com.gh.common.prioritychain -import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import com.gh.common.dialog.ReserveDialog import com.gh.gamecenter.common.entity.SimpleGameEntity -import com.gh.gamecenter.message.MessageUnreadRepository +import com.gh.gamecenter.core.utils.CurrentActivityHolder class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private var mFragment: Fragment? = null private var mReserveData: List? = null /** * 提前预处理显示弹窗的内容 */ - fun doPreProcess(fragment: Fragment, reserveData: List?) { - mFragment = fragment + fun doPreProcess(reserveData: List?) { mReserveData = reserveData if (getStatus() == STATUS_PENDING) { @@ -34,18 +32,21 @@ class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { } override fun onProcess() { - when (getStatus()) { - STATUS_VALID -> { - val reserveDialog = ReserveDialog.getInstance(mReserveData!!) - reserveDialog.setOnDismissListener { - MessageUnreadRepository.loadMessageUnreadData() + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { + val reserveDialog = ReserveDialog.getInstance(mReserveData!!) + reserveDialog.setOnDismissListener { + processNext() + } + reserveDialog.show((currentActivity as FragmentActivity).supportFragmentManager, "reserveDialog") + } + + STATUS_INVALID -> { processNext() } - reserveDialog.show(mFragment!!.childFragmentManager, "reserveDialog") - } - - STATUS_INVALID -> { - processNext() } } } diff --git a/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt index 8d4061e0de..737c917fa1 100644 --- a/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt @@ -1,16 +1,40 @@ package com.gh.common.prioritychain -import android.content.Context -import com.gh.gamecenter.manager.UpdateManager +import androidx.fragment.app.FragmentActivity +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.update.UpdateHelper -class UpdateDialogHandler(context: Context, priority: Int) : PriorityChainHandler(priority) { +class UpdateDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private val mUpdateManager = UpdateManager.getInstance(context) + fun doPreProcess() { + UpdateHelper.getUpdate(false) { + if (getStatus() == STATUS_PENDING) { + if (UpdateHelper.isUpdateValid(false)) { + updateStatus(STATUS_VALID) + onProcess() + } else { + updateStatus(STATUS_INVALID) + processNext() + } + } else { + if (UpdateHelper.isUpdateValid(false)) { + updateStatus(STATUS_VALID) + } else { + updateStatus(STATUS_INVALID) + } + } + } + } override fun onProcess() { - mUpdateManager.checkUpdate(true, null) - mUpdateManager.setDismissCallback { - processNext() + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + if (getStatus() == STATUS_VALID) { + UpdateHelper.showUpdateDialog(currentActivity as FragmentActivity) { + processNext() + } + } } } diff --git a/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt index 11ce439917..56f5cd5706 100644 --- a/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt @@ -1,21 +1,20 @@ package com.gh.common.prioritychain import android.graphics.Bitmap -import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import com.gh.gamecenter.common.callback.BiCallback import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.gamecenter.feature.entity.WelcomeDialogEntity import com.gh.gamecenter.fragment.MainWrapperViewModel import com.gh.gamecenter.fragment.WelcomeDialogFragment import com.halo.assistant.HaloApp -class WelcomeDialogHandler(priority: Int): PriorityChainHandler(priority) { +class WelcomeDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private var mFragment: Fragment? = null private var mWelcomeDialogEntity: WelcomeDialogEntity? = null - fun doPreProcess(fragment: Fragment, welcomeDialogEntity: WelcomeDialogEntity?) { - mFragment = fragment + fun doPreProcess(welcomeDialogEntity: WelcomeDialogEntity?) { mWelcomeDialogEntity = welcomeDialogEntity val preLoadClosure = { @@ -57,21 +56,21 @@ class WelcomeDialogHandler(priority: Int): PriorityChainHandler(priority) { } override fun onProcess() { - when (getStatus()) { - STATUS_VALID -> { - if (mFragment == null || !mFragment!!.isAdded) { - updateStatus(STATUS_INVALID) - processNext() - } else { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { val welcomeDialog = WelcomeDialogFragment.getInstance(mWelcomeDialogEntity) welcomeDialog.setOnDismissListener { processNext() } - welcomeDialog.show(mFragment!!.childFragmentManager, "WelcomeDialog") + welcomeDialog.show((currentActivity as FragmentActivity).supportFragmentManager, "WelcomeDialog") + } + + STATUS_INVALID -> { + processNext() } - } - STATUS_INVALID -> { - processNext() } } } diff --git a/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt b/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt index f4d75ab36b..1dd72c10f0 100644 --- a/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt @@ -1,16 +1,30 @@ package com.gh.common.provider import android.content.Context -import android.os.Handler +import androidx.fragment.app.FragmentActivity import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.common.util.DialogUtils import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.provider.IUpdateManagerProvider -import com.gh.gamecenter.manager.UpdateManager +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.update.UpdateHelper @Route(path = RouteConsts.provider.updateManager, name = "UpdateManager暴露服务") class UpdateManagerProviderImpl: IUpdateManagerProvider { - override fun checkUpdate(context: Context, isAutoCheck: Boolean, handler: Handler?) { - UpdateManager.getInstance(context).checkUpdate(isAutoCheck, handler) + override fun checkUpdate(activity: FragmentActivity, ignoreSuppressOption: Boolean) { + val dialog = DialogUtils.showWaitDialog(activity, "检查更新中...") + + UpdateHelper.getUpdate(ignoreSuppressOption) { + dialog.dismiss() + + if (UpdateHelper.isUpdateValid(ignoreSuppressOption)) { + UpdateHelper.showUpdateDialog(activity) { + // Do nothing + } + } else { + ToastUtils.toast("您的光环助手已是最新版本") + } + } } override fun init(context: Context?) { diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index dfd97d8585..66f98de019 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -48,6 +48,7 @@ import com.gh.common.constant.Config; import com.gh.common.filter.RegionSettingHelper; import com.gh.common.history.HistoryDatabase; import com.gh.common.history.HistoryHelper; +import com.gh.common.prioritychain.GlobalPriorityChainHelper; import com.gh.common.repository.ReservationRepository; import com.gh.common.simulator.SimulatorGameManager; import com.gh.common.util.AdHelper; @@ -590,10 +591,12 @@ public class MainActivity extends BaseActivity { ExtensionsKt.removeFromParent(startSdkAdIcpContainer, true); } - // 通知优先级高的弹窗可以显示了 - AppExecutor.getUiExecutor().execute(() -> { - mMainWrapperFragment.showDialog(); - }); + onSplashHidden(); + } + + private void onSplashHidden() { + // 通知全局弹窗可以进行显示 + AppExecutor.getUiExecutor().execute(GlobalPriorityChainHelper.INSTANCE::start); } /** diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt index 2ef3c4dee3..9c813e82fc 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt @@ -29,7 +29,6 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode private var mHomeTabPosition: Int = -1 private var mHomeTab: SubjectRecommendEntity? = null - private var mFloatingWindowHandler: FloatingWindowHandler? = null var appBarOffset = 0 @@ -116,12 +115,4 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode }) } - fun setFloatingWindowHandler(handler: FloatingWindowHandler) { - mFloatingWindowHandler = handler - } - - fun getFloatingWindowHandler(): FloatingWindowHandler? { - return mFloatingWindowHandler - } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt index 1206823161..f956887fa9 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt @@ -59,10 +59,8 @@ import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFrag import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailFragment import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment import com.gh.gamecenter.login.entity.UserInfoEntity -import com.gh.gamecenter.message.MessageUnreadRepository import com.gh.gamecenter.message.MessageUnreadViewModel import com.gh.gamecenter.personal.HaloPersonalFragment -import com.gh.gamecenter.pkg.PkgHelper import com.gh.gamecenter.servers.GameServersPublishFragment import com.gh.gamecenter.servers.GameServersTestFragment import com.gh.gamecenter.subject.SubjectFragment @@ -352,50 +350,24 @@ class MainWrapperFragment : BaseFragment_ViewPager_Checkable(), OnBackPressedLis /** * 构建 PriorityChain,使用优先队列来决定当前需要显示哪一个弹窗 - * 正常弹窗顺序(不含首页自动下拉二楼),更新弹窗、隐私政策弹窗、消息通知权限弹窗、预约弹窗、插件化通知 popup、启动弹窗、右下悬浮窗 - * 特殊弹窗顺序(含首页自动下拉二楼,无需请求显示启动弹窗),更新弹窗、隐私政策弹窗、消息通知权限弹窗、预约弹窗、插件化通知 popup、首页自动下拉二楼、右下悬浮窗 + * 正常弹窗顺序(不含首页自动下拉二楼),更新弹窗、隐私政策弹窗、消息通知权限弹窗、预约弹窗、插件化通知 popup、右下悬浮窗 + * 特殊弹窗顺序(含首页自动下拉二楼,无需请求显示启动弹窗) 预约弹窗、插件化通知 popup、首页自动下拉二楼、右下悬浮窗 * 可以根据上述的顺序为弹窗预设优先级 - * 更新弹窗 (-100) - * 隐私政策弹窗 (-99) - * 消息通知权限弹窗 (0) - * 预约弹窗 (1) * 插件化通知 popup (2) * 首页自动下拉二楼 (3) - * 启动弹窗 (4) * 首页右下悬浮窗 (5) */ private fun buildPriorityChain() { val homeSearchToolWrapperViewModel: HomeSearchToolWrapperViewModel = viewModelProviderFromParent() - val updateDialogHandler = UpdateDialogHandler(requireContext(), -100) - val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99) - val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0) - val reserveDialogHandler = ReserveDialogHandler(1) val accelerateNotificationHandler = AccelerateNotificationHandler(2) val homePushHandler = HomePushHandler(3) - val welcomeDialogHandler = WelcomeDialogHandler(4) val floatingWindowHandler = FloatingWindowHandler(5) - homeSearchToolWrapperViewModel.setFloatingWindowHandler(floatingWindowHandler) - - mPriorityChain.addHandler(updateDialogHandler) - mPriorityChain.addHandler(privacyPolicyDialogHandler) - mPriorityChain.addHandler(welcomeDialogHandler) - mPriorityChain.addHandler(reserveDialogHandler) - mPriorityChain.addHandler(notificationPermissionDialogHandler) mPriorityChain.addHandler(accelerateNotificationHandler) mPriorityChain.addHandler(homePushHandler) mPriorityChain.addHandler(floatingWindowHandler) - mViewModel?.privacyPolicyDialog?.observe(this) { - privacyPolicyDialogHandler.doPreProcess(requireActivity(), it) - } - - mViewModel?.reserveDialog?.observe(this) { - reserveDialogHandler.doPreProcess(this, it) - MessageUnreadRepository.loadMessageUnreadData() - } - mViewModel?.accelerateNotificationPopup?.observe(this) { accelerateNotificationHandler.doPreProcess( requireActivity(), @@ -416,48 +388,22 @@ class MainWrapperFragment : BaseFragment_ViewPager_Checkable(), OnBackPressedLis && "on" == homeDataEntity.homePush?.popSwitch && !homePushSet.contains(homeDataEntity.homePush?.id) - if (mPopUpHomePush) { - welcomeDialogHandler.doPreProcess(this, null) - } else { - welcomeDialogHandler.doPreProcess(this, mViewModel!!.welcomeDialog.value) - } - homePushHandler.doPreProcess(mHomeFragment!!, mPopUpHomePush) } } - fun showDialog() { - mPriorityChain.start() + override fun onResume() { + super.onResume() + + // 若全局优先队列里的 handler 已经处理完毕,且本页面优先队列中还有未处理的 handler,则开始处理当前页面的 handler + if (GlobalPriorityChainHelper.isGlobalHandlerQueueEmpty() + && !mPriorityChain.isHandlerQueueEmpty()) { + mPriorityChain.start() + } } fun isPriorityChainHandlerQueueEmpty() = mPriorityChain.isHandlerQueueEmpty() - private fun applyPkgConfig() { - val pkgLinkEntity = PkgHelper.getPkgConfig() - if (pkgLinkEntity != null) { - val bottomTab = pkgLinkEntity.homeBottomTab - if (!pkgLinkEntity.shouldStayAtMainActivity) { - // 不停留在首页,执行跳转,标记已用 - PkgHelper.markConfigUsed() - DirectUtils.directToLinkPage(requireContext(), pkgLinkEntity, "推广包配置", "首页") - } else if ("home" != bottomTab) { - // 停留首页,但选中底部 tab 不是首页的,执行选中,标记已用 - PkgHelper.markConfigUsed() - // TODO 根据具体 tab 来作为跳转的具体位置,避免硬编码 - var targetIndex = INDEX_HOME - when (bottomTab) { - "game_lib" -> targetIndex = INDEX_GAME - "community" -> targetIndex = INDEX_BBS - "video" -> targetIndex = INDEX_VIDEO - "gh" -> targetIndex = INDEX_PERSONAL - } - mViewPager.currentItem = targetIndex - onPageChanged(targetIndex) - changeColor(targetIndex) - } - } - } - private fun updateGameBarContent(navBarEntity: SubjectRecommendEntity?) { if (navBarEntity != null) { mBinding.mainTabGame.visibility = View.VISIBLE @@ -508,9 +454,6 @@ class MainWrapperFragment : BaseFragment_ViewPager_Checkable(), OnBackPressedLis } }) } - applyPkgConfig() - - updateRealNameErrorContainer() } override fun handleOnClick(view: View): Boolean { diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt index 30030d9cb0..91869dd529 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt @@ -1,23 +1,15 @@ package com.gh.gamecenter.fragment -import android.annotation.SuppressLint import android.app.Application -import android.preference.PreferenceManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.alibaba.android.arouter.launcher.ARouter import com.gh.common.util.CheckLoginUtils -import com.gh.common.util.PackageUtils -import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.common.entity.SimpleGameEntity -import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.core.provider.IFloatingWindowProvider -import com.gh.gamecenter.core.utils.SPUtils -import com.gh.gamecenter.entity.DialogEntity import com.gh.gamecenter.entity.SubjectRecommendEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.WelcomeDialogEntity @@ -36,14 +28,10 @@ import retrofit2.HttpException class MainWrapperViewModel(application: Application, repository: MainWrapperRepository) : AndroidViewModel(application) { private val mApi = RetrofitManager.getInstance().api - private val mSensitiveApi = RetrofitManager.getInstance().api val gameNavBar: MutableLiveData = repository.getGameNavBarLiveData() val videoNavBar: MutableLiveData = repository.getVideoNavBarLiveData() - val welcomeDialog = MutableLiveData() - val reserveDialog = MutableLiveData?>() - val privacyPolicyDialog = MutableLiveData() val accelerateNotificationPopup = MutableLiveData?>() val floatingWindow = MutableLiveData?>() @@ -51,96 +39,10 @@ class MainWrapperViewModel(application: Application, repository: MainWrapperRepo * 请求各种弹窗的数据 */ fun requestAllDialogData() { - requestOpeningData() - requestReserveDialog() requestAccelerateNotificationPopup() requestFloatingWindowList() } - /** - * 获取弹窗 - */ - @SuppressLint("CheckResult") - private fun requestOpeningData() { - - val sp = PreferenceManager.getDefaultSharedPreferences(getApplication()) - - val lastId = SPUtils.getString(sp, Constants.SP_LAST_OPENING_ID, "") - val lastTime = SPUtils.getLong(sp, Constants.SP_LAST_OPENING_TIME, 0) - val openType = if (HaloApp.getInstance().isNewForThisVersion) "first" else "not_first_time" - - mSensitiveApi.getOpeningDialog(HaloApp.getInstance().channel, lastId, lastTime, openType) - .subscribeOn(Schedulers.io()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: DialogEntity) { - val welcomeDialogEntity = data.welcomeDialog - val privacyPolicyDialogEntity = data.privacyPolicyDialog - - // 类型为游戏时判断是否本地已安装该游戏,已安装不弹弹窗 - if (welcomeDialogEntity != null) { - updateWelcomeDialogData(null, welcomeDialogEntity) - if (welcomeDialogEntity.type == "game") { - if (welcomeDialogEntity.packages == null) { - welcomeDialog.postValue(data.welcomeDialog) - } else { - for (packageName in data.welcomeDialog.packages!!) { - if (PackageUtils.isInstalled(getApplication(), packageName)) { - welcomeDialog.postValue(null) - return - } - } - welcomeDialog.postValue(data.welcomeDialog) - } - } else { - welcomeDialog.postValue(data.welcomeDialog) - } - } - - // 全新安装忽略隐私弹窗 - if (privacyPolicyDialogEntity != null) { - val id = privacyPolicyDialogEntity.id - val lastAcceptedId = SPUtils.getString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, "") - if (HaloApp.getInstance().isBrandNewInstall) { - SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, id) - privacyPolicyDialog.postValue(null) - } else { - if (id != lastAcceptedId) { - privacyPolicyDialog.postValue(privacyPolicyDialogEntity) - } else { - privacyPolicyDialog.postValue(null) - } - } - } else { - privacyPolicyDialog.postValue(null) - } - } - - override fun onFailure(exception: Exception) { - privacyPolicyDialog.postValue(null) - } - }) - } - - @SuppressLint("CheckResult") - private fun requestReserveDialog() { - if (CheckLoginUtils.isLogin()) { - mApi.getReserveDialog(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - reserveDialog.postValue(data) - } - - override fun onFailure(exception: Exception) { - reserveDialog.postValue(null) - } - }) - } else { - reserveDialog.postValue(null) - } - } - fun requestAccelerateNotificationPopup() { if (CheckLoginUtils.isLogin()) { mApi.getAccelerateNotificationPopup(UserManager.getInstance().userId) @@ -169,32 +71,13 @@ class MainWrapperViewModel(application: Application, repository: MainWrapperRepo floatingWindowProvider?.getFloatingWindowOnly { anies, throwable -> if (anies != null) { - updateWelcomeDialogData(anies as ArrayList, null) - floatingWindow.postValue(anies) + floatingWindow.postValue(anies as ArrayList?) } else if (throwable != null) { floatingWindow.postValue(arrayListOf()) } } } - /** - * 更新启动弹窗是否需要显示消失动画的标记 - * 当启动弹窗绑定的 windowId 与右下角悬浮窗的第一个 window 的 id 一样时才显示消失动画 - */ - private fun updateWelcomeDialogData( - floatingWindowList: ArrayList?, - welcomeDialogEntity: WelcomeDialogEntity? - ) { - val finalFloatingWindowList = floatingWindowList ?: floatingWindow.value - val finalWelcomeDialogEntity = welcomeDialogEntity ?: welcomeDialog.value - - if (finalFloatingWindowList != null && finalWelcomeDialogEntity != null) { - if (finalFloatingWindowList.firstOrNull()?.id == finalWelcomeDialogEntity.floatingWindowId) { - finalWelcomeDialogEntity.shouldShowExitAnimation = true - } - } - } - fun postMessageRead(messageId: String) { val jsonObject = JSONObject() jsonObject.put("type", "system_message") diff --git a/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt new file mode 100644 index 0000000000..bbd2f40abb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt @@ -0,0 +1,318 @@ +package com.gh.gamecenter.fragment + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.text.Html +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import com.gh.common.util.DirectUtils +import com.gh.common.util.PackageInstaller +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.CustomLinkMovementMethod +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.databinding.AppUpdateHintDialogBinding +import com.gh.gamecenter.databinding.AppUpdatingDialogBinding +import com.gh.gamecenter.databinding.DialogUpdateBinding +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.update.UpdateHelper +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import java.text.DecimalFormat +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.roundToInt + +/** + * 更新弹窗 DialogFragment + */ +class UpdateDialogFragment : BaseDialogFragment() { + + private var mUpdateEntity: AppEntity? = null + private var mDismissCallback: EmptyCallback? = null + private val mIsDismissByTouchInside = AtomicBoolean(false) + private val mBinding by lazy { DialogUpdateBinding.inflate(layoutInflater) } + + private var mIsDisplayingDownloadingStyle = false // 是否正在显示更新中样式 + + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.name.contains("光环助手")) { + if (mIsDisplayingDownloadingStyle) { + updateDownloadingView( + mBinding.updatingContainerView, + downloadEntity, + mUpdateEntity!! + ) + } else { + if (DownloadStatus.done == downloadEntity.status) { + updateUpdateHintView( + mBinding.updateHintContainerView, + mUpdateEntity!!, + UpdateHelper.isUpdateFileDownloaded(mUpdateEntity!!) + ) + } + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + + DownloadManager.getInstance().addObserver(mDataWatcher) + + mUpdateEntity = arguments?.getParcelable(UPDATE_ENTITY) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + showUpdateHintStyle(requireContext(), mUpdateEntity!!) + + return mBinding.root + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + val cancelable = !mUpdateEntity!!.isForce + + setCancelable(cancelable) + setCanceledOnTouchOutside(cancelable) + this@UpdateDialogFragment.isCancelable = cancelable + } + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + if (!mIsDismissByTouchInside.get()) { + SensorsBridge.trackVersionUpdateDialogClick("关闭弹窗") + } + + mDismissCallback?.onCallback() + } + + override fun onDestroyView() { + super.onDestroyView() + + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + + /** + * 显示更新弹窗(提示样式) + * @param context 上下文 + * @param updateEntity 更新实体 + */ + private fun showUpdateHintStyle(context: Context, updateEntity: AppEntity) { + mIsDisplayingDownloadingStyle = false + + val updateHintBinding = mBinding.updateHintContainerView + val updatingBinding = mBinding.updatingContainerView + + updatingBinding.root.visibility = View.GONE + updateHintBinding.root.visibility = View.VISIBLE + + val cancelUpdateTextView = updateHintBinding.cancel + val confirmTextView = updateHintBinding.confirm + + if (!TextUtils.isEmpty(updateEntity.spareLink)) { + val defaultText = "部分设备若无法更新安装,请前往官网进行下载安装:" + val externalTextTv = updateHintBinding.externalTextTv + val builder = SpanBuilder(defaultText + updateEntity.spareLink) + .click( + context, + defaultText.length, + defaultText.length + updateEntity.spareLink!!.length, + R.color.text_theme, + false + ) { + DirectUtils.directToExternalBrowser(context, updateEntity.spareLink!!) + } + .build() + externalTextTv.text = builder + externalTextTv.movementMethod = CustomLinkMovementMethod.getInstance() + externalTextTv.visibility = View.VISIBLE + } + + val updateFileIsDownloaded = UpdateHelper.isUpdateFileDownloaded(updateEntity) + + if (NetworkUtils.isWifiConnected(context)) { + if (!updateFileIsDownloaded) { + updateUpdateHintView(updateHintBinding, updateEntity, false) + UpdateHelper.createUpdate(updateEntity, true) + } else { + updateUpdateHintView(updateHintBinding, updateEntity, true) + } + } else { + updateUpdateHintView(updateHintBinding, updateEntity, updateFileIsDownloaded) + } + + updateHintBinding.desc.text = Html.fromHtml(updateEntity.content) + updateHintBinding.version.text = java.lang.String.format("版本%s更新日志:", updateEntity.version) + updateHintBinding.size.text = java.lang.String.format("大小 %s", updateEntity.size) + + cancelUpdateTextView.setOnClickListener { + if (updateEntity.isForce) { + UpdateHelper.exitApp() + } else { + dismiss() + } + mIsDismissByTouchInside.set(true) + val cancelText: String = cancelUpdateTextView.text.toString() + SensorsBridge.trackVersionUpdateDialogClick(cancelText) + } + + confirmTextView.setOnClickListener { + // 产品不接受显示下载完成文案从立即更新变为立即安装,所以文案为立即更新时一律不执行安装 + if (UpdateHelper.isUpdateFileDownloaded(updateEntity) && confirmTextView.text != "立即更新") { + DataLogUtils.uploadUpgradeLog(context, "install") //上传更新安装数据 + PackageInstaller.install(context, false, UpdateHelper.getUpdateDownloadPath(updateEntity), null) + } else { + showDownloadingStyle(updateEntity) + } + + val buttonName: String = confirmTextView.text.toString() + SensorsBridge.trackVersionUpdateDialogClick(buttonName) + } + + SensorsBridge.trackVersionUpdateDialogShow() + + DataLogUtils.uploadUpgradeLog(context, "notice") //上传更新通知弹窗数据 + } + + /** + * 更新更新提示的文案 + */ + private fun updateUpdateHintView( + binding: AppUpdateHintDialogBinding, + updateEntity: AppEntity, + isUpdateDownloaded: Boolean + ) { + val confirmText = if (isUpdateDownloaded) "立即安装" else "立即更新" + val cancelText = if (isUpdateDownloaded) { + if (updateEntity.isForce) "暂不安装,退出光环" else "暂不安装" + } else { + if (updateEntity.isForce) "暂不更新,退出光环" else "暂不更新" + } + + binding.confirm.text = confirmText + binding.downloadedHint.visibility = if (isUpdateDownloaded) View.VISIBLE else View.GONE + binding.cancel.text = cancelText + } + + /** + * 显示更新下载中弹窗 + */ + private fun showDownloadingStyle(updateEntity: AppEntity) { + mIsDisplayingDownloadingStyle = true + + val updatingBinding = mBinding.updatingContainerView + val updateHintBinding = mBinding.updateHintContainerView + + updatingBinding.root.visibility = View.VISIBLE + updateHintBinding.root.visibility = View.GONE + + updatingBinding.appTvCancel.setOnClickListener { + DownloadManager.getInstance().cancel(updateEntity.url) + if (updateEntity.isForce) { + UpdateHelper.exitApp() + } else { + dismiss() + } + } + + if (NetworkUtils.isMobileConnected(context)) { + ToastUtils.toast("当前使用移动数据进行下载") + } + + UpdateHelper.createUpdate(updateEntity, false) + } + + @SuppressLint("SetTextI18n") + private fun updateDownloadingView( + dialogBinding: AppUpdatingDialogBinding, + downloadEntity: DownloadEntity, + updateEntity: AppEntity + ) { + // 被取消任务的状态不用来更新页面 + if (DownloadStatus.cancel != downloadEntity.status) { + val size = (downloadEntity.progress / 1024) / 1024F + val df = DecimalFormat("0.00") + dialogBinding.size.text = (df.format(size) + "MB") + dialogBinding.remain.text = String.format( + "剩余%s", + SpeedUtils.getRemainSecondTime( + downloadEntity.size, + downloadEntity.progress, + downloadEntity.speed * 1024 + ) + ) + dialogBinding.progress.progress = (downloadEntity.percent * 10).roundToInt() + + val width = dialogBinding.progress.width + val marLeft = (downloadEntity.percent / 100 * width) + val anchorLp = dialogBinding.progressAnchor.layoutParams + if (anchorLp is ConstraintLayout.LayoutParams) { + anchorLp.leftMargin = marLeft.roundToInt() + dialogBinding.progressAnchor.layoutParams = anchorLp + } + + val fillingLp = dialogBinding.progressFilling.layoutParams + fillingLp.width = (marLeft + DisplayUtils.dip2px(5F)).toInt() + dialogBinding.progressFilling.layoutParams = fillingLp + + dialogBinding.percent.text = "${downloadEntity.percent} %" + } + + if (DownloadStatus.done == downloadEntity.status) { + DownloadManager.getInstance().cancel(downloadEntity.url, false, true, false) + try { + dismiss() + } catch (ignored: IllegalArgumentException) { + // do nothing + } + if (updateEntity.isForce) { + AppExecutor.uiExecutor.executeWithDelay({ UpdateHelper.exitApp() }, 1000L) + } + } else if (DownloadStatus.neterror == downloadEntity.status) { + ToastUtils.toast("网络错误,请稍后重试") + } else if (DownloadStatus.diskisfull == downloadEntity.status) { + ToastUtils.toast("磁盘已满,请清理后重试") + } else if (DownloadStatus.diskioerror == downloadEntity.status) { + ToastUtils.toast("磁盘 IO 异常,请稍后重试") + } else if (DownloadStatus.timeout == downloadEntity.status) { + ToastUtils.toast("请求超时,请稍后重试") + } else if (DownloadStatus.notfound == downloadEntity.status) { + ToastUtils.toast("下载链接异常,请稍后重试") + } else if (DownloadStatus.hijack == downloadEntity.status) { + ToastUtils.toast("网络劫持,请稍后重试") + } + } + + companion object { + const val UPDATE_ENTITY = "update_entity" + + fun newInstance(updateEntity: AppEntity, dismissCallback: EmptyCallback): UpdateDialogFragment { + val fragment = UpdateDialogFragment() + val bundle = Bundle() + bundle.putParcelable(UPDATE_ENTITY, updateEntity) + fragment.arguments = bundle + fragment.mDismissCallback = dismissCallback + return fragment + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index 1801149595..9d2b4cb683 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -160,8 +160,6 @@ class HomeFragment : LazyFragment() { HomeItemGameTestV2ViewHolderWatcher() ) - mHomeSearchViewModel.getFloatingWindowHandler()?.setView(this, mBinding.gameList) - mBinding.gameList.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) 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 adfc5db9df..e69de29bb2 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java @@ -1,637 +0,0 @@ -package com.gh.gamecenter.manager; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.preference.PreferenceManager; -import android.text.Html; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.app.NotificationManagerCompat; - -import com.gh.common.exposure.ExposureUtils; -import com.gh.common.util.DialogUtils; -import com.gh.common.util.DirectUtils; -import com.gh.common.util.PackageInstaller; -import com.gh.common.util.PackageUtils; -import com.gh.download.DownloadManager; -import com.gh.gamecenter.MainActivity; -import com.gh.gamecenter.R; -import com.gh.gamecenter.common.constant.Constants; -import com.gh.gamecenter.common.retrofit.Response; -import com.gh.gamecenter.common.utils.DataLogUtils; -import com.gh.gamecenter.common.utils.ExtensionsKt; -import com.gh.gamecenter.common.utils.NetworkUtils; -import com.gh.gamecenter.common.utils.SensorsBridge; -import com.gh.gamecenter.common.view.CustomLinkMovementMethod; -import com.gh.gamecenter.core.AppExecutor; -import com.gh.gamecenter.core.utils.CurrentActivityHolder; -import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.core.utils.EmptyCallback; -import com.gh.gamecenter.core.utils.GsonUtils; -import com.gh.gamecenter.core.utils.MD5Utils; -import com.gh.gamecenter.core.utils.MtaHelper; -import com.gh.gamecenter.core.utils.SPUtils; -import com.gh.gamecenter.core.utils.SpanBuilder; -import com.gh.gamecenter.core.utils.SpeedUtils; -import com.gh.gamecenter.entity.AppEntity; -import com.gh.gamecenter.feature.entity.GameEntity; -import com.gh.gamecenter.feature.exposure.ExposureEvent; -import com.gh.gamecenter.retrofit.RetrofitManager; -import com.gh.ndownload.NDataChanger; -import com.halo.assistant.HaloApp; -import com.lightgame.download.DataWatcher; -import com.lightgame.download.DownloadEntity; -import com.lightgame.download.DownloadStatus; -import com.lightgame.download.FileUtils; -import com.lightgame.utils.AppManager; -import com.lightgame.utils.Utils; - -import java.io.File; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import retrofit2.HttpException; - -// 吐了,这个祖传的类,代码写得不是一般的糟糕,还不敢随便改 : ( - -/** - * Created by LGT on 2016/12/8. - * 助手更新 工具类 - */ -public class UpdateManager { - - // 用于控制第二次打开更新弹窗 - private final static String ONCE_ONLY_SECOND_DEFAULT = "ONCE_ONLY_SECOND_DEFAULT"; - private final static String ONCE_ONLY_SECOND_CLOSE = "ONCE_ONLY_SECOND_CLOSE"; - private final static String ONCE_ONLY_SECOND_OPEN = "ONCE_ONLY_SECOND_OPEN"; - - private Context mContext; - private Context mApplicationContext; - - private SharedPreferences mSp; - - private AppEntity appEntity; - - private EmptyCallback mOnDismissCallback; - - private Dialog downloadDialog; - private Dialog loadingDialog; - private Dialog updateDialog; - - private ProgressBar app_pb_progress; - private TextView appProgressSize; - private TextView appProgressRemain; - private TextView appProgressPercent; - private View appProgressFilling; - private View appProgressAnchor; - - private TextView cancelUpdateTextView; - private TextView downloadedHintView; - private TextView confirmTextView; - - private boolean isShowDownload; - private boolean isChecking; - private long lastUpdateFileSize; - - private String currentUpdateMd5; - - private DataWatcher dataWatcher = new DataWatcher() { - @Override - public void onDataChanged(DownloadEntity downloadEntity) { - if (downloadEntity.getName().contains("光环助手")) { - if (downloadDialog != null && downloadDialog.isShowing() && isShowDownload) { - - // 被取消任务的状态不用来更新页面 - if (!DownloadStatus.cancel.equals(downloadEntity.getStatus())) { - float size = (((float) downloadEntity.getProgress() / 1024) / 1024); - DecimalFormat df = new DecimalFormat("0.00"); - appProgressSize.setText((df.format(size) + "MB")); - appProgressRemain.setText(String.format("剩余%s", SpeedUtils.getRemainSecondTime(downloadEntity.getSize(), - downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024))); - app_pb_progress.setProgress((int) (downloadEntity.getPercent() * 10)); - - int width = app_pb_progress.getWidth(); - int marLeft = (int) (downloadEntity.getPercent() / 100 * width); - ViewGroup.LayoutParams anchorLp = appProgressAnchor.getLayoutParams(); - if (anchorLp instanceof ConstraintLayout.LayoutParams) { - ((ConstraintLayout.LayoutParams) anchorLp).leftMargin = marLeft; - appProgressAnchor.setLayoutParams(anchorLp); - } - - if (downloadEntity.getSize() != lastUpdateFileSize) { - lastUpdateFileSize = downloadEntity.getSize(); - SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); - } - - ViewGroup.LayoutParams fillingLp = appProgressFilling.getLayoutParams(); - fillingLp.width = marLeft + DisplayUtils.dip2px(5); - appProgressFilling.setLayoutParams(fillingLp); - - appProgressPercent.setText(((int) downloadEntity.getPercent() + "%")); - } - - if (DownloadStatus.done.equals(downloadEntity.getStatus())) { - DownloadManager.getInstance().cancel(downloadEntity.getUrl(), false, true, false); - if (downloadDialog != null) { - try { - downloadDialog.dismiss(); - } catch (IllegalArgumentException ignored) { - // do nothing - } - } - if (appEntity != null && appEntity.isForce()) { - AppExecutor.getUiExecutor().executeWithDelay(() -> exitApp(), 1000); - } - } else if (DownloadStatus.neterror.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "网络错误,请稍后重试"); - } else if (DownloadStatus.diskisfull.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "磁盘已满,请清理后重试"); - } else if (DownloadStatus.diskioerror.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "磁盘 IO 异常,请稍后重试"); - } else if (DownloadStatus.timeout.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "请求超时,请稍后重试"); - } else if (DownloadStatus.notfound.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "下载链接异常,请稍后重试"); - } else if (DownloadStatus.hijack.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "网络劫持,请稍后重试"); - } - } else { - if (downloadEntity.getSize() != lastUpdateFileSize) { - lastUpdateFileSize = downloadEntity.getSize(); - SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); - } - - if (updateDialog != null && updateDialog.isShowing()) { - // 强制更新的时候完成下载不用更新页面 - if (DownloadStatus.done.equals(downloadEntity.getStatus()) && !appEntity.isForce()) { - updateUpdateDialogView(true); - } - } else { - if (mContext instanceof MainActivity - && AppManager.getInstance().getRecentActiveActivity() == mContext) { - if (DownloadStatus.done.equals(downloadEntity.getStatus())) { - showUpdateDialog(currentUpdateMd5); - } - } - } - } - } - } - }; - - private UpdateManager(Context context) { - mContext = context; - mApplicationContext = context.getApplicationContext(); - mSp = PreferenceManager.getDefaultSharedPreferences(mApplicationContext); - - this.isShowDownload = false; - this.isChecking = false; - this.loadingDialog = null; - - lastUpdateFileSize = SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0L); - } - - public static UpdateManager getInstance(Context context) { - return new UpdateManager(context); - } - - // 检查更新 - public void checkUpdate(final boolean isAutoCheck, final Handler handler) { - if (isChecking) { - return; - } - isChecking = true; - if (!isAutoCheck) { - loadingDialog = DialogUtils.showWaitDialog(mContext, "检查更新中..."); - } - String channel = HaloApp.getInstance().getChannel(); - RetrofitManager.getInstance().getApi().getUpdate( - PackageUtils.getGhVersionName(), - PackageUtils.getGhVersionCode(), - channel, - Build.VERSION.SDK_INT) - .map(appEntity -> { - boolean isShowUpdateDialog = false; - - if (appEntity.getVersionCode() > PackageUtils.getGhVersionCode()) { - // 助手有更新 - UpdateManager.this.appEntity = appEntity; - - // 手动更新 强制更新 每次都提示 - if (!isAutoCheck || "EVERY_TIME_OPEN".equals(appEntity.getAlert())) { - isShowUpdateDialog = true; - } else if ("ONCE_ONLY".equals(appEntity.getAlert())) { - if (mSp.getBoolean(getUpdateOnceOnlySpKey(), true)) { - isShowUpdateDialog = true; - mSp.edit().putBoolean(getUpdateOnceOnlySpKey(), false).apply(); - } - } else if ("ONCE_ONLY_SECOND".equals(appEntity.getAlert())) { - String onceOnlySecond = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT); - if (ONCE_ONLY_SECOND_OPEN.equals(onceOnlySecond)) { - isShowUpdateDialog = true; - mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_CLOSE).apply(); - } - - if (ONCE_ONLY_SECOND_DEFAULT.equals(onceOnlySecond)) { - mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_OPEN).apply(); - } - } else if (!"NEVER".equals(appEntity.getAlert())) { // 一天提示一次 - String showUpdateTime = mSp.getString("show_update_time", null); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - String today = format.format(new Date()); - if (!today.equals(showUpdateTime)) { - isShowUpdateDialog = true; - mSp.edit().putString("show_update_time", today).apply(); - } - } - } - return isShowUpdateDialog ? MD5Utils.getUpdateMD5(appEntity.getUrl(), appEntity.getContent()) : null; - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Response() { - @Override - public void onResponse(String response) { - isChecking = false; - currentUpdateMd5 = response; - if (loadingDialog != null) { - loadingDialog.dismiss(); - } - if (response != null) { - if (!appEntity.isForce() - && isAutoCheck - && !isUpdateFileDownloaded(response) - && NetworkUtils.isWifiConnected(mApplicationContext)) { - createUpdate(response, true); - } else { - showUpdateDialog(response); - } - if (handler != null) { - Message message = new Message(); - message.what = 0; - message.obj = appEntity.getVersion(); - handler.sendMessage(message); - } - } else if (!isAutoCheck) { - Utils.toast(mApplicationContext, "已是最新版本"); - if (handler != null) { - handler.sendEmptyMessage(1); - } - - invokeDismissCallback(); - } - } - - @Override - public void onFailure(HttpException e) { - isChecking = false; - if (loadingDialog != null) { - loadingDialog.dismiss(); - } - if (!isAutoCheck) { - if (handler != null) { - handler.sendEmptyMessage(1); - } - if (e != null && (e.code() == 304 || e.code() == 404)) { - Utils.toast(mApplicationContext, "您的光环助手已是最新版本"); - return; - } - - Utils.toast(mApplicationContext, "检查更新失败"); - } - - invokeDismissCallback(); - } - }); - } - - // 显示助手有更新提示框 - private void showUpdateDialog(final String md5) { - Context context = getValidContext(); - - AtomicBoolean dismissByTouchInside = new AtomicBoolean(false); - - updateDialog = new Dialog(context); - updateDialog.setOnDismissListener(dialog -> { - invokeDismissCallback(); - if (!isShowDownload) { - DownloadManager.getInstance().removeObserver(dataWatcher); - } - if (!dismissByTouchInside.get() && appEntity != null) { - SensorsBridge.trackVersionUpdateDialogClick( - "关闭弹窗", - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭" - ); - } - }); - Window window = updateDialog.getWindow(); - if (window != null) { - window.setBackgroundDrawableResource(android.R.color.transparent); - } - - View view = View.inflate(context, R.layout.app_update_hint_dialog, null); - - cancelUpdateTextView = view.findViewById(R.id.cancel); - downloadedHintView = view.findViewById(R.id.downloadedHint); - confirmTextView = view.findViewById(R.id.confirm); - - if (!TextUtils.isEmpty(appEntity.getSpareLink())) { - String defaultText = "部分设备若无法更新安装,请前往官网进行下载安装:"; - TextView externalTextTv = view.findViewById(R.id.externalTextTv); - SpannableStringBuilder builder = - new SpanBuilder(defaultText + appEntity.getSpareLink()) - .click(mContext, - defaultText.length(), - defaultText.length() + appEntity.getSpareLink().length(), - R.color.text_theme, - false, - () -> { - DirectUtils.directToExternalBrowser(context, appEntity.getSpareLink()); - return null; - }) - .build(); - externalTextTv.setText(builder); - externalTextTv.setMovementMethod(CustomLinkMovementMethod.getInstance()); - externalTextTv.setVisibility(View.VISIBLE); - } - - if (NetworkUtils.isWifiConnected(context)) { - if (!isUpdateFileDownloaded(md5)) { - updateUpdateDialogView(false); - createUpdate(md5, true); - } else { - updateUpdateDialogView(true); - } - } else { - updateUpdateDialogView(isUpdateFileDownloaded(md5)); - } - - TextView content = view.findViewById(R.id.desc); - content.setText(Html.fromHtml(appEntity.getContent())); - - TextView version = view.findViewById(R.id.version); - version.setText(String.format("版本%s更新日志:", appEntity.getVersion())); - - TextView size = view.findViewById(R.id.size); - size.setText(String.format("大小 %s", appEntity.getSize())); - - view.setOnClickListener(v -> { - if (appEntity.isForce()) { - exitApp(); - } else { - updateDialog.dismiss(); - } - }); - - cancelUpdateTextView.setOnClickListener(v -> { - dismissByTouchInside.set(true); - if (appEntity.isForce()) { - String cancelText = cancelUpdateTextView.getText().toString(); - SensorsBridge.trackVersionUpdateDialogClick( - cancelText, - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭"); - AppExecutor.getUiExecutor().executeWithDelay(this::exitApp, 500L); - } else { - updateDialog.dismiss(); - } - }); - - confirmTextView.setOnClickListener(v -> { - if (!isUpdateFileDownloaded(md5)) { - dismissByTouchInside.set(true); - updateDialog.dismiss(); - } else if (isUpdateFileDownloaded(md5) && !appEntity.isForce()) { - dismissByTouchInside.set(true); - updateDialog.dismiss(); - } - String path = FileUtils.getDownloadPath(context, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - // 产品不接受显示下载完成文案从立即更新变为立即安装,所以文案为立即更新时一律不执行安装 - if (isUpdateFileDownloaded(md5) && confirmTextView.getText() != "立即更新") { - DataLogUtils.uploadUpgradeLog(context, "install"); //上传更新安装数据 - PackageInstaller.install(context, false, path, null); - } else { - MtaHelper.onEvent("软件更新", "下载开始"); - - showDownloadDialog(md5); - } - String buttonName = confirmTextView.getText().toString(); - SensorsBridge.trackVersionUpdateDialogClick( - buttonName, - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭"); - }); - - if (appEntity.isForce()) { - updateDialog.setCanceledOnTouchOutside(false); - updateDialog.setCancelable(false); - } - updateDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - updateDialog.setContentView(view); - updateDialog.show(); - - SensorsBridge.trackVersionUpdateDialogShow(appEntity.getAlert(), appEntity.isForce() ? "关闭且强退" : "仅关闭"); - - DataLogUtils.uploadUpgradeLog(context, "notice"); //上传更新通知弹窗数据 - } - - private void exitApp() { - NotificationManagerCompat.from(mApplicationContext).cancelAll(); - AppManager.getInstance().finishAllActivity(); - } - - private void showDownloadDialog(String md5) { - Context context = getValidContext(); - - if (NetworkUtils.isMobileConnected(context)) { - Utils.toast(context, "当前使用移动数据进行下载"); - } - - downloadDialog = new Dialog(context); - downloadDialog.setOnDismissListener(dialog -> { - invokeDismissCallback(); - }); - Window window = downloadDialog.getWindow(); - if (window != null) { - window.setBackgroundDrawableResource(android.R.color.transparent); - } - - View view = View.inflate(context, R.layout.app_updating_dialog, null); - - app_pb_progress = view.findViewById(R.id.progress); - appProgressSize = view.findViewById(R.id.size); - appProgressRemain = view.findViewById(R.id.remain); - appProgressAnchor = view.findViewById(R.id.progress_anchor); - appProgressPercent = view.findViewById(R.id.percent); - appProgressFilling = view.findViewById(R.id.progress_filling); - - view.findViewById(R.id.app_tv_cancel).setOnClickListener(v -> { - DownloadManager.getInstance().cancel(appEntity.getUrl()); - if (appEntity.isForce()) { - exitApp(); - } else { - downloadDialog.dismiss(); - } - }); - - downloadDialog.setOnDismissListener(dialog -> { - DownloadManager.getInstance().removeObserver(dataWatcher); - isShowDownload = false; - }); - - int dialogWidth = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60); - downloadDialog.setCanceledOnTouchOutside(false); - downloadDialog.setCancelable(false); - downloadDialog.closeOptionsMenu(); - downloadDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - downloadDialog.setContentView(view, new ViewGroup.LayoutParams(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - - isShowDownload = true; - - createUpdate(md5, false); - - downloadDialog.show(); - } - - private void createUpdate(String md5, boolean isSilentUpdate) { - DownloadManager.getInstance().addObserver(dataWatcher); - boolean shouldCancelPreviousDownload = true; // 是否应该取消旧更新任务 - - // 在部分设备上取消正在进行中的旧下载任务再创建新下载任务有机率造成新下载任务依然带有 "静默更新" 标签导致无法唤起安装 - // 所以这里对当前静默更新任务正在进行中的时候就不用删旧任务,直接改 meta 标签 - if (!isSilentUpdate - && DownloadManager.getInstance().isTaskDownloading(appEntity.getUrl())) { - try { - DownloadEntity entity = NDataChanger.INSTANCE.getDownloadEntries().get(appEntity.getUrl()); - - if (entity != null) { - ExtensionsKt.addMetaExtra(entity, Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新"); - DownloadManager.getInstance().updateDownloadEntity(entity); - DownloadManager.getInstance().resume(entity, false); - - shouldCancelPreviousDownload = false; - } - } catch (Exception e) { - // 出现异常走删旧下载任务重下流程 - shouldCancelPreviousDownload = true; - e.printStackTrace(); - } - } - - // 预下载完成或者还没进行预下载的都进这里,先删掉旧的下载文件再进行下载 - if (shouldCancelPreviousDownload) { - String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - File file = new File(path); - if (file.exists() && file.delete()) { - Utils.log(file.getName() + " file delete success."); - } - - ExposureEvent event = ExposureUtils.logADownloadExposureEvent( - new GameEntity(Constants.GHZS_GAME_ID, "光环助手V" + appEntity.getVersion()), - null, - null, - ExposureUtils.DownloadType.DOWNLOAD); - - DownloadEntity downloadEntity = new DownloadEntity(); - downloadEntity.setUrl(appEntity.getUrl()); - downloadEntity.setName("光环助手V" + appEntity.getVersion()); - downloadEntity.setPath(path); - downloadEntity.setPlatform("官方版"); - downloadEntity.setGameId(Constants.GHZS_GAME_ID); - downloadEntity.setFormat("apk"); - downloadEntity.setExposureTrace(GsonUtils.toJson(event)); - - if (isSilentUpdate) { - ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SILENT_UPDATE); - } - - downloadEntity.setPackageName(mApplicationContext.getPackageName()); - DownloadManager.getInstance().cancel(appEntity.getUrl(), true, true, false); - DownloadManager.getInstance().pauseAll(); - - AppExecutor.getUiExecutor().executeWithDelay(() -> { - DownloadManager.getInstance().add(downloadEntity); - }, 200); - } - } - - /** - * 尽量获取有效的 activity context - */ - private Context getValidContext() { - Context context = mContext; - - Activity currentActivity = CurrentActivityHolder.getCurrentActivity(); - if (currentActivity != null - && mContext != currentActivity - && !currentActivity.isFinishing()) { - context = currentActivity; - } - - return context; - } - - private boolean isUpdateFileDownloaded(String md5) { - String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - File file = new File(path); - - return file.exists() && file.length() == SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0); - } - - private void updateUpdateDialogView(boolean isUpdateDownloaded) { - if (isUpdateDownloaded) { - confirmTextView.setText("立即安装"); - downloadedHintView.setVisibility(View.VISIBLE); - if (appEntity.isForce()) { - cancelUpdateTextView.setText("暂不安装,退出光环"); - } else { - cancelUpdateTextView.setText("暂不安装"); - } - } else { - confirmTextView.setText("立即更新"); - downloadedHintView.setVisibility(View.GONE); - if (appEntity.isForce()) { - cancelUpdateTextView.setText("暂不更新,退出光环"); - } else { - cancelUpdateTextView.setText("暂不更新"); - } - } - } - - public void setDismissCallback(EmptyCallback callback) { - mOnDismissCallback = callback; - } - - private void invokeDismissCallback() { - if (mOnDismissCallback != null && (appEntity == null || !appEntity.isForce())) { - mOnDismissCallback.onCallback(); - } - } - - private String getUpdateOnceOnlySpKey() { - return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode(); - } - - private String getUpdateOnceOnlySecondSpKey() { - return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode(); - } - -} diff --git a/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt b/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt new file mode 100644 index 0000000000..b92dd0d44b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt @@ -0,0 +1,264 @@ +package com.gh.gamecenter.update + +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import androidx.fragment.app.FragmentActivity +import com.gh.common.exposure.ExposureUtils +import com.gh.common.exposure.ExposureUtils.logADownloadExposureEvent +import com.gh.common.util.PackageUtils +import com.gh.download.DownloadManager +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.addMetaExtra +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.GsonUtils.toJson +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.fragment.UpdateDialogFragment +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.ndownload.NDataChanger +import com.halo.assistant.HaloApp +import com.lightgame.download.DownloadEntity +import com.lightgame.download.FileUtils +import com.lightgame.utils.AppManager +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import retrofit2.HttpException +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +/** + * 应用更新的辅助类 + */ +object UpdateHelper { + + private const val ONCE_ONLY_SECOND_DEFAULT = "ONCE_ONLY_SECOND_DEFAULT" + private const val ONCE_ONLY_SECOND_CLOSE = "ONCE_ONLY_SECOND_CLOSE" + private const val ONCE_ONLY_SECOND_OPEN = "ONCE_ONLY_SECOND_OPEN" + + private const val ALERT_EVERY_TIME_OPEN = "EVERY_TIME_OPEN" // 每次启动都提示 + private const val ALERT_ONCE_ONLY = "ONCE_ONLY" // 仅一次 + private const val ALERT_ONCE_ONLY_SECOND = "ONCE_ONLY_SECOND" // 仅一次(第二次打开) + private const val ALERT_NEVER = "NEVER" // 从不 + + private const val SP_KEY_SHOW_UPDATE_TIME = "show_update_time" // 上次显示更新弹窗的时间 + + // 缓存的更新实体 + private var mCachedUpdateEntity: AppEntity? = null + private val mApi by lazy { RetrofitManager.getInstance().api } + private val mSp by lazy { HaloApp.getInstance().getSharedPreferences("update", Context.MODE_PRIVATE) } + + private val mVersionName by lazy { +// "5.3.0" + PackageUtils.getGhVersionName() + } + + private val mVersionCode by lazy { +// 410 + PackageUtils.getGhVersionCode() + } + + /** + * 检查更新 + * @param dumpCache 是否清除更新的实体缓存 (重新检查更新) + * @param resultCallback 结果回调,无论成功或失败都会回调,可在此 callback 检查是否有更新 + */ + fun getUpdate(dumpCache: Boolean, resultCallback: EmptyCallback) { + if (dumpCache) { + mCachedUpdateEntity = null + } else if (mCachedUpdateEntity != null) { + // 若缓存的更新实体不为空,则直接使用缓存的更新实体 + resultCallback.onCallback() + return + } + + val channel: String = HaloApp.getInstance().channel + mApi.getUpdate(mVersionName, mVersionCode, channel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onFailure(e: HttpException?) { + super.onFailure(e) + resultCallback.onCallback() + } + + override fun onResponse(response: AppEntity?) { + super.onResponse(response) + +// response?.isForce = true +// response?.alert = ALERT_EVERY_TIME_OPEN + + mCachedUpdateEntity = response + + resultCallback.onCallback() + } + }) + } + + /** + * 更新是否有效 + * @param ignoreSuppressOption 是否忽略提示 + */ + fun isUpdateValid(ignoreSuppressOption: Boolean): Boolean { + val updateEntity = mCachedUpdateEntity ?: return false + + if (updateEntity.versionCode >= mVersionCode) { + if (ignoreSuppressOption || ALERT_EVERY_TIME_OPEN == updateEntity.alert) { + // 手动检查更新或者后台配置每次启动都提示 + return true + } else if (ALERT_ONCE_ONLY == updateEntity.alert) { + // 仅显示一次更新 + if (mSp.getBoolean(getUpdateOnceOnlySpKey(), true)) { + return true + } + } else if (ALERT_ONCE_ONLY_SECOND == updateEntity.alert) { + // 仅显示一次更新(第二次打开) + val onceOnlySecond: String? = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT) + if (ONCE_ONLY_SECOND_OPEN == onceOnlySecond) { + return true + } + } else if (ALERT_NEVER != updateEntity.alert) { // 一天提示一次 + val showUpdateTime: String? = mSp.getString(SP_KEY_SHOW_UPDATE_TIME, null) + val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val today: String = format.format(Date()) + if (today != showUpdateTime) { + return true + } + } + } + return false + } + + /** + * 标记更新的使用情况 + */ + private fun markUpdate(updateEntity: AppEntity) { + if (ALERT_ONCE_ONLY == updateEntity.alert) { + // 仅显示一次更新 + mSp.edit().putBoolean(getUpdateOnceOnlySpKey(), false).apply() + } else if (ALERT_ONCE_ONLY_SECOND == updateEntity.alert) { + // 仅显示一次更新(第二次打开) + val onceOnlySecond: String? = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT) + if (ONCE_ONLY_SECOND_OPEN == onceOnlySecond) { + mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_CLOSE).apply() + } + if (ONCE_ONLY_SECOND_DEFAULT == onceOnlySecond) { + mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_OPEN).apply() + } + } else if (ALERT_NEVER != updateEntity.alert) { // 一天提示一次 + val showUpdateTime: String? = mSp.getString(SP_KEY_SHOW_UPDATE_TIME, null) + val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val today: String = format.format(Date()) + if (today != showUpdateTime) { + mSp.edit().putString(SP_KEY_SHOW_UPDATE_TIME, today).apply() + } + } + } + + /** + * 显示更新弹窗 + */ + fun showUpdateDialog(activity: FragmentActivity, callback: EmptyCallback) { + if (mCachedUpdateEntity == null) { + callback.onCallback() + return + } + + UpdateDialogFragment + .newInstance(mCachedUpdateEntity!!, callback) + .show(activity.supportFragmentManager, "update") + + markUpdate(mCachedUpdateEntity!!) + } + + /** + * 创建下载任务 + */ + fun createUpdate(updateEntity: AppEntity, isSilentUpdate: Boolean) { + var shouldCancelPreviousDownload = true // 是否应该取消旧更新任务 + + // 在部分设备上取消正在进行中的旧下载任务再创建新下载任务有机率造成新下载任务依然带有 "静默更新" 标签导致无法唤起安装 + // 所以这里对当前静默更新任务正在进行中的时候就不用删旧任务,直接改 meta 标签 + if (!isSilentUpdate + && DownloadManager.getInstance().isTaskDownloading(updateEntity.url) + ) { + try { + val entity = NDataChanger.downloadEntries[updateEntity.url] + if (entity != null) { + entity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新") + DownloadManager.getInstance().updateDownloadEntity(entity) + DownloadManager.getInstance().resume(entity, false) + shouldCancelPreviousDownload = false + } + } catch (e: Exception) { + // 出现异常走删旧下载任务重下流程 + shouldCancelPreviousDownload = true + e.printStackTrace() + } + } + + // 预下载完成或者还没进行预下载的都进这里,先删掉旧的下载文件再进行下载 + if (shouldCancelPreviousDownload) { + val path = getUpdateDownloadPath(updateEntity) + val file = File(path) + if (file.exists() && file.delete()) { + Utils.log(file.name + " file delete success.") + } + val event = logADownloadExposureEvent( + GameEntity(Constants.GHZS_GAME_ID, "光环助手V" + updateEntity.version), + null, + null, + ExposureUtils.DownloadType.DOWNLOAD + ) + val downloadEntity = DownloadEntity() + downloadEntity.url = updateEntity.url + downloadEntity.name = "光环助手V" + updateEntity.version + downloadEntity.path = path + downloadEntity.platform = "官方版" + downloadEntity.gameId = Constants.GHZS_GAME_ID + downloadEntity.exposureTrace = toJson(event) + downloadEntity.format = "apk" + if (isSilentUpdate) { + downloadEntity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, Constants.SILENT_UPDATE) + } + downloadEntity.packageName = HaloApp.getInstance().packageName + DownloadManager.getInstance().cancel(downloadEntity.url, true, true, false) + DownloadManager.getInstance().pauseAll() + AppExecutor.uiExecutor.executeWithDelay({ + DownloadManager.getInstance().add(downloadEntity) + }, 200) + } + } + + /** + * 更新文件是否已下载 + */ + fun isUpdateFileDownloaded(updateEntity: AppEntity): Boolean { + val file = File(getUpdateDownloadPath(updateEntity)) + + return file.exists() && PackageUtils.getPackageNameByPath(HaloApp.getInstance(), file.path) != null + } + + private fun getUpdateOnceOnlySpKey(): String { + return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode() + } + + private fun getUpdateOnceOnlySecondSpKey(): String { + return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode() + } + + fun exitApp() { + NotificationManagerCompat.from(HaloApp.getInstance()).cancelAll() + AppManager.getInstance().finishAllActivity() + } + + fun getUpdateDownloadPath(updateEntity: AppEntity): String { + val md5 = MD5Utils.getUpdateMD5(updateEntity.url, updateEntity.content) + + return FileUtils.getDownloadPath(HaloApp.getInstance(), "光环助手V" + updateEntity.version + "_" + md5 + ".apk") + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/app_update_hint_dialog.xml b/app/src/main/res/layout/app_update_hint_dialog.xml index 57f3eb9b10..c9e1d7e3fe 100644 --- a/app/src/main/res/layout/app_update_hint_dialog.xml +++ b/app/src/main/res/layout/app_update_hint_dialog.xml @@ -2,7 +2,7 @@ + + + + + + + \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt b/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt index be5bfe27b7..12cf0f417e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt @@ -36,6 +36,15 @@ class NotificationHintDialogFragment : BaseDialogFragment() { private var mDismissCallback: (() -> Unit)? = null private val mBinding: DialogNotificationHintBinding by lazy { DialogNotificationHintBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return mBinding.root } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt index a588ab0530..02e484235e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt @@ -19,6 +19,7 @@ import android.widget.RelativeLayout import android.widget.TextView import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity import androidx.viewbinding.ViewBinding import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.R @@ -793,11 +794,13 @@ object DialogHelper { confirmText = context.getString(R.string.unsupported_feature_dialog_confirm_text), cancelText = context.getString(R.string.unspported_feature_dialog_cancel_text), confirmClickCallback = { - val updateManager = ARouter - .getInstance() - .build(RouteConsts.provider.updateManager) - .navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(context, false, null) + if (context is FragmentActivity) { + val updateManager = ARouter + .getInstance() + .build(RouteConsts.provider.updateManager) + .navigation() as? IUpdateManagerProvider + updateManager?.checkUpdate(context, false) + } }, cancelClickCallback = {}, extraConfig = Config(centerTitle = true, centerContent = true) diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt index ea1b18fe78..2c3c6a3ab1 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt @@ -1,9 +1,8 @@ package com.gh.gamecenter.core.provider -import android.content.Context -import android.os.Handler +import androidx.fragment.app.FragmentActivity import com.alibaba.android.arouter.facade.template.IProvider interface IUpdateManagerProvider: IProvider { - fun checkUpdate(context: Context, isAutoCheck: Boolean, handler: Handler?) + fun checkUpdate(activity: FragmentActivity, ignoreSuppressOption: Boolean) } \ No newline at end of file diff --git a/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt b/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt index e80ee4d17b..37037f2624 100644 --- a/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt +++ b/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt @@ -56,7 +56,7 @@ class AboutFragment : ToolbarFragment() { val updateManager = ARouter.getInstance().build(RouteConsts.provider.updateManager).navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(requireContext(), false, mBaseHandler) // 检查更新 + updateManager?.checkUpdate(requireActivity(), true) // 检查更新 mBinding.aboutGhIcon.setOnLongClickListener { MtaHelper.onEvent("我的光环_设置", "关于光环", "图标长按") @@ -124,7 +124,7 @@ class AboutFragment : ToolbarFragment() { root.setOnClickListener { MtaHelper.onEvent("我的光环_设置", "关于光环", "版本更新") val updateManager = ARouter.getInstance().build(RouteConsts.provider.updateManager).navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(requireContext(), false, mBaseHandler) // 检查更新 + updateManager?.checkUpdate(requireActivity(), true) // 检查更新 } } mBinding.userProtocolItem.run { diff --git a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt index de0846fa3f..5b88db3f33 100644 --- a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt +++ b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt @@ -67,8 +67,7 @@ class ComposeAboutActivity : ComposeBaseActivity() { } updateManager?.checkUpdate( this@ComposeAboutActivity, - false, - updateHandler + true, ) // 检查更新 Scaffold( @@ -148,8 +147,7 @@ class ComposeAboutActivity : ComposeBaseActivity() { ) { updateManager?.checkUpdate( this@ComposeAboutActivity, - false, - updateHandler + true, ) // 检查更新 } SettingDivider()