feat: APP内容配置重构-附属需求:弹窗顺序 https://jira.shanqu.cc/browse/GHZS-4086

This commit is contained in:
chenjuntao
2023-11-28 17:18:30 +08:00
committed by 曾祥俊
parent 3109b16753
commit ae5ef2e7bf
26 changed files with 995 additions and 905 deletions

View File

@ -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?,

View File

@ -24,6 +24,15 @@ class ReserveDialog : BaseDialogFragment() {
private lateinit var mReserveList: List<SimpleGameEntity>
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)

View File

@ -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<DialogEntity>() {
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<List<SimpleGameEntity>>() {
override fun onSuccess(data: List<SimpleGameEntity>) {
reserveDialogHandler.doPreProcess(data)
}
override fun onFailure(exception: Exception) {
reserveDialogHandler.doPreProcess(null)
}
})
} else {
reserveDialogHandler.doPreProcess(null)
}
}
}

View File

@ -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()
}
}
}

View File

@ -7,16 +7,32 @@ class PriorityChain {
private val mHandlerQueue: Queue<PriorityChainHandler> = 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()
}

View File

@ -52,7 +52,8 @@ abstract class PriorityChainHandler(private val mPriority: Int) : Comparable<Pri
fun processNext() {
Utils.log(TAG, "${javaClass.simpleName} processNext $mStatus")
mQueue?.poll()?.process(mQueue!!)
mQueue?.remove(this)
mQueue?.peek()?.process(mQueue!!)
}
override fun compareTo(other: PriorityChainHandler): Int {

View File

@ -2,18 +2,17 @@ package com.gh.common.prioritychain
import androidx.fragment.app.FragmentActivity
import com.gh.common.dialog.PrivacyPolicyDialogFragment
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.entity.DialogEntity
class PrivacyPolicyDialogHandler(priority: Int) : PriorityChainHandler(priority) {
private var mActivity: FragmentActivity? = null
private var mPrivacyPolicyEntity: DialogEntity.PrivacyPolicyEntity? = null
/**
* 提前预处理显示弹窗的内容
*/
fun doPreProcess(fragmentActivity: FragmentActivity, privacyPolicyEntity: DialogEntity.PrivacyPolicyEntity?) {
mActivity = fragmentActivity
fun doPreProcess(privacyPolicyEntity: DialogEntity.PrivacyPolicyEntity?) {
mPrivacyPolicyEntity = privacyPolicyEntity
if (getStatus() == STATUS_PENDING) {
@ -33,15 +32,20 @@ class PrivacyPolicyDialogHandler(priority: Int) : PriorityChainHandler(priority)
}
override fun onProcess() {
when(getStatus()) {
STATUS_VALID -> {
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()
}
}
}

View File

@ -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<SimpleGameEntity>? = null
/**
* 提前预处理显示弹窗的内容
*/
fun doPreProcess(fragment: Fragment, reserveData: List<SimpleGameEntity>?) {
mFragment = fragment
fun doPreProcess(reserveData: List<SimpleGameEntity>?) {
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()
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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()
}
}
}

View File

@ -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?) {

View File

@ -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);
}
/**

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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<SubjectRecommendEntity?> = repository.getGameNavBarLiveData()
val videoNavBar: MutableLiveData<SubjectRecommendEntity?> = repository.getVideoNavBarLiveData()
val welcomeDialog = MutableLiveData<WelcomeDialogEntity?>()
val reserveDialog = MutableLiveData<List<SimpleGameEntity>?>()
val privacyPolicyDialog = MutableLiveData<DialogEntity.PrivacyPolicyEntity?>()
val accelerateNotificationPopup = MutableLiveData<List<GameEntity>?>()
val floatingWindow = MutableLiveData<ArrayList<FloatingWindowEntity>?>()
@ -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<DialogEntity>() {
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<List<SimpleGameEntity>>() {
override fun onSuccess(data: List<SimpleGameEntity>) {
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<FloatingWindowEntity>, null)
floatingWindow.postValue(anies)
floatingWindow.postValue(anies as ArrayList<FloatingWindowEntity>?)
} else if (throwable != null) {
floatingWindow.postValue(arrayListOf())
}
}
}
/**
* 更新启动弹窗是否需要显示消失动画的标记
* 当启动弹窗绑定的 windowId 与右下角悬浮窗的第一个 window 的 id 一样时才显示消失动画
*/
private fun updateWelcomeDialogData(
floatingWindowList: ArrayList<FloatingWindowEntity>?,
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")

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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<String>() {
@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();
}
}

View File

@ -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<AppEntity>() {
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")
}
}

View File

@ -2,7 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
@ -126,12 +126,12 @@
<TextView
android:id="@+id/confirm"
style="@style/PrimaryGradientButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginTop="10dp"
android:layout_marginRight="24dp"
style="@style/PrimaryGradientButton"
android:gravity="center"
android:includeFontPadding="false"
android:paddingTop="12.5dp"

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/updateHintContainerView"
layout="@layout/app_update_hint_dialog" />
<include
android:id="@+id/updatingContainerView"
layout="@layout/app_updating_dialog"
android:visibility="gone" />
</RelativeLayout>

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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 {

View File

@ -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()