package com.gh.common.dialog import android.animation.ValueAnimator import android.content.Context import android.content.DialogInterface import android.content.pm.PackageInfo import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.LinearInterpolator import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DirectUtils import com.gh.common.util.LogUtils import com.gh.common.util.PackageHelper import com.gh.common.util.PackageUtils import com.gh.download.DownloadManager import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.CustomLinkMovementMethod import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.SpanBuilder import com.gh.gamecenter.databinding.FragmentPackageCheckBinding import com.gh.gamecenter.databinding.PackageCheckItemBinding import com.gh.gamecenter.feature.entity.DetectionObjectEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PackageDialogEntity import com.gh.gamecenter.eventbus.EBPackage import com.halo.assistant.HaloApp import com.lightgame.adapter.BaseRecyclerAdapter import com.lightgame.dialog.BaseDialogFragment import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus import io.reactivex.disposables.Disposable import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode /** * 包名检测弹窗 */ // TODO 将 gameEntity 放到 argument 里再取出,避免重建时为空 class PackageCheckDialogFragment : BaseDialogFragment() { private lateinit var binding: FragmentPackageCheckBinding private var mTotalWidth = 0f private val mDuration = 3000 private var mDisposable: Disposable? = null private var mAdapter: PackageCheckAdapter? = null private var mAllInstalledPackages = PackageHelper.getInstalledPackageNameList(HaloApp.getInstance().application, 0) var gameEntity: GameEntity? = null var callBack: ConfirmListener? = null private var mDismissByTouchInside = false private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { val packageName = downloadEntity.packageName val detectionObjects = gameEntity?.packageDialog?.detectionObjects if (DownloadStatus.add == downloadEntity.status || DownloadStatus.done == downloadEntity.status) { detectionObjects?.forEach { detectionObject -> if (detectionObject.packages.contains(packageName)) { val packageLink = gameEntity?.packageDialog?.links?.find { it.buttonLink } LogUtils.uploadPackageCheck( "pkg_check_pop_download", if (DownloadStatus.add == downloadEntity.status) "下载开始" else "下载完成", gameEntity, packageLink?.text ?: "", packageLink?.title ?: "", downloadEntity.gameId, downloadEntity.getMetaExtra(Constants.GAME_NAME) ) } } } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) EventBus.getDefault().register(this) gameEntity?.let { LogUtils.uploadPackageCheck("pkg_check_pop_click", "出现弹窗", it, "", "", "", "") SensorsBridge.trackPkgCheckDialogShow( gameId = it.id, gameName = it.name ?: "", gameType = it.categoryChinese ) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = FragmentPackageCheckBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) gameEntity?.packageDialog?.let { changeParams(it.detectionObjects.size) binding.packageRv.layoutManager = LinearLayoutManager(requireContext()) mAdapter = PackageCheckAdapter(requireContext(), it.detectionObjects) binding.packageRv.adapter = mAdapter binding.titleTv.text = it.title binding.contentTv.text = it.content val spanBuilder = SpanBuilder(it.linkHintText).build() it.links.forEachIndexed { index, link -> val linkSpan = SpanBuilder(link.title ?: "").click( requireContext(), 0, (link.title ?: "").length, com.gh.gamecenter.common.R.color.text_theme, true ) { LogUtils.uploadPackageCheck( "pkg_check_pop_click", "点击链接", gameEntity, link.text, link.title, "", "" ) SensorsBridge.trackPkgCheckDialogClick( buttonName = "点击链接", gameId = gameEntity?.id ?: "", gameName = gameEntity?.name ?: "", gameType = gameEntity?.categoryChinese ?: "", isNotPrompt = if (binding.noRemindAgainCb.isVisible) { binding.noRemindAgainCb.isChecked } else null, linkId = link.link ?: "", linkType = link.type ?: "", linkText = link.linkText ?: "" ) DirectUtils.directToLinkPage(requireContext(), link, "包名检测弹窗", "") }.build() spanBuilder.append(linkSpan) if (index != it.links.size - 1) { spanBuilder.append("、") } } binding.linkHintTv.text = spanBuilder binding.linkHintTv.movementMethod = CustomLinkMovementMethod.getInstance() when (it.level) { "HINT_SKIP" -> { binding.cancelTv.text = "取消" binding.noRemindAgainCb.visibility = View.GONE } "ALWAYS_HINT" -> { binding.cancelTv.text = "我知道了" binding.noRemindAgainCb.visibility = View.GONE } "OPTIONAL_CURRENT_HINT" -> { binding.cancelTv.text = "我知道了" binding.noRemindAgainCb.visibility = View.VISIBLE } else -> { binding.cancelTv.text = "我知道了" binding.noRemindAgainCb.visibility = View.VISIBLE } } initListener(it) } binding.root.post { checkPackage() } } private fun changeParams(size: Int) { val params = binding.packageRv.layoutParams as LinearLayout.LayoutParams params.height = if (size > 3) (28f.dip2px() * 3.5).toInt() else 28f.dip2px() * size binding.packageRv.layoutParams = params } private fun initListener(entity: PackageDialogEntity) { binding.downloadBtn.setOnClickListener { if (binding.noRemindAgainCb.isChecked) { saveRecord(entity) } val isAllPackageInstalled = isAllPackageInstalled(mAllInstalledPackages, entity) if (isAllPackageInstalled) { mDismissByTouchInside = true callBack?.onConfirm() dismissAllowingStateLoss() } else { var packageLink = getNotInstalledLink(entity) if (packageLink == null) { packageLink = entity.links.find { it.buttonLink } } if (packageLink != null) { LogUtils.uploadPackageCheck( "pkg_check_pop_click", "点击前往下载", gameEntity, packageLink.text, packageLink.title, "", "" ) SensorsBridge.trackPkgCheckDialogClick( buttonName = "点击前往下载", gameId = gameEntity?.id ?: "", gameName = gameEntity?.name ?: "", gameType = gameEntity?.categoryChinese ?: "", isNotPrompt = if (binding.noRemindAgainCb.isVisible) { binding.noRemindAgainCb.isChecked } else null ) DirectUtils.directToLinkPage(requireContext(), packageLink, "包名检测弹窗", "") } } } binding.cancelTv.setOnClickListener { if (entity.level != "HINT_SKIP") { callBack?.onConfirm() } if (binding.noRemindAgainCb.isChecked) { saveRecord(entity) LogUtils.uploadPackageCheck("pkg_check_pop_click", "不再提示", gameEntity, "", "", "", "") } mDismissByTouchInside = true SensorsBridge.trackPkgCheckDialogClick( buttonName = binding.cancelTv.text.toString(), gameId = gameEntity?.id ?: "", gameName = gameEntity?.name ?: "", gameType = gameEntity?.categoryChinese ?: "", isNotPrompt = if (binding.noRemindAgainCb.isVisible) { binding.noRemindAgainCb.isChecked } else null ) dismissAllowingStateLoss() } } private fun saveRecord(entity: PackageDialogEntity) { if (entity.level == "OPTIONAL_CURRENT_HINT") { SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity?.id}", true) } else { SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity?.packageDialog?.id}", true) } } private fun checkPackage() { var index = 0 mTotalWidth = (DisplayUtils.getScreenWidth() - 108f.dip2px()).toFloat() mDisposable = rxTimer(1) { gameEntity?.packageDialog?.detectionObjects?.let { objects -> if (objects.isNotEmpty()) { val averageTime = if (objects.size == 1) { mDuration } else { mDuration / objects.size } if (it != 0L && it % averageTime == 0L && index < objects.size) { mAdapter?.notifyPackages() binding.packageRv.smoothScrollToPosition(index) index++ } } } if (it >= mDuration) { mDisposable?.dispose() binding.downloadBtn.isEnabled = true binding.downloadBtn.background = com.gh.gamecenter.common.R.drawable.bg_notification_open_btn_style_2.toDrawable() } } val animator = ValueAnimator.ofInt(0, 100) animator.duration = mDuration.toLong() animator.interpolator = LinearInterpolator() animator.addUpdateListener { binding.progressBar.progress = it.animatedValue as Int } animator.start() } private fun getNotInstalledLink(packageDialogEntity: PackageDialogEntity): LinkEntity? { val links = LinkedHashSet() packageDialogEntity.detectionObjects.forEach { obj -> if (!checkDetectionsInstalled(mAllInstalledPackages, obj.packages)) { obj.assignDownload.forEach { links.add(packageDialogEntity.links[it]) } } } var link: LinkEntity? = null if (links.size > 1) { link = links.find { it.buttonLink } ?: links.toList()[0] } else if (links.size == 1) { link = links.toList()[0] } return link } override fun onStart() { super.onStart() val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px() val height = ViewGroup.LayoutParams.WRAP_CONTENT requireDialog().window?.setLayout(width, height) requireDialog().setCanceledOnTouchOutside(true) DownloadManager.getInstance().addObserver(dataWatcher) } override fun onResume() { super.onResume() mAllInstalledPackages = PackageHelper.getInstalledPackageNameList(HaloApp.getInstance().application, 0) gameEntity?.packageDialog?.let { if (isAllPackageInstalled(mAllInstalledPackages, it)) { callBack?.onConfirm() dismissAllowingStateLoss() } } } override fun onDestroyView() { super.onDestroyView() EventBus.getDefault().unregister(this) if (mDisposable?.isDisposed == false) { mDisposable?.dispose() } LogUtils.uploadPackageCheck("pkg_check_pop_click", "关闭弹窗", gameEntity, "", "", "", "") DownloadManager.getInstance().removeObserver(dataWatcher) } override fun onDismiss(dialog: DialogInterface) { if (!mDismissByTouchInside) { SensorsBridge.trackPkgCheckDialogClick( buttonName = "关闭弹窗", gameId = gameEntity?.id ?: "", gameName = gameEntity?.name ?: "", gameType = gameEntity?.categoryChinese ?: "", isNotPrompt = if (binding.noRemindAgainCb.isVisible) { binding.noRemindAgainCb.isChecked } else null ) } super.onDismiss(dialog) } //安装、卸载事件 @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(busFour: EBPackage) { if (busFour.isInstalledOrUninstalled()) { mAllInstalledPackages = PackageHelper.getInstalledPackageNameList(HaloApp.getInstance().application, 0) mAdapter?.notifyDataSetChanged() } } inner class PackageCheckAdapter(val context: Context, val entities: ArrayList) : BaseRecyclerAdapter(context) { private var index = -1 fun notifyPackages() { index++ notifyItemChanged(index) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return PackageCheckViewHolder(parent.toBinding()) } override fun getItemCount(): Int = entities.size override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is PackageCheckViewHolder) { val entity = entities[position] holder.binding.gameNameTv.text = entity.text if (position <= index) { val isAllInstalled = checkDetectionsInstalled(mAllInstalledPackages, entity.packages) if (isAllInstalled) { holder.binding.statusTv.text = "已安装" holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, com.gh.gamecenter.common.R.color.text_theme)) } else { holder.binding.statusTv.text = "未安装" holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, com.gh.gamecenter.common.R.color.secondary_red)) } holder.binding.statusTv.visibility = View.VISIBLE } else { holder.binding.statusTv.visibility = View.GONE } } } } class PackageCheckViewHolder(val binding: PackageCheckItemBinding) : BaseRecyclerViewHolder(binding.root) companion object { @JvmStatic fun show(activity: AppCompatActivity, gameEntity: GameEntity, callBack: ConfirmListener) { val packageDialogEntity = gameEntity.packageDialog if (packageDialogEntity == null) { callBack.onConfirm() return } val allInstalledPackages = PackageHelper.getInstalledPackageNameList(HaloApp.getInstance().application, 0) if (isAllPackageInstalled(allInstalledPackages, packageDialogEntity)) { callBack.onConfirm() return } val isChoose = SPUtils.getBoolean("${Constants.SP_PACKAGE_CHECK}:${packageDialogEntity.id}", false) if (packageDialogEntity.level == "OPTIONAL_HINT" && isChoose) { callBack.onConfirm() return } val isCurrentGameChoose = SPUtils.getBoolean("${Constants.SP_PACKAGE_CHECK}:${gameEntity.id}", false) if (packageDialogEntity.level == "OPTIONAL_CURRENT_HINT" && isCurrentGameChoose) { callBack.onConfirm() return } if (!activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) return var dialogFragment = activity.supportFragmentManager.findFragmentByTag(PackageCheckDialogFragment::class.java.name) as? PackageCheckDialogFragment if (dialogFragment == null) { dialogFragment = PackageCheckDialogFragment() dialogFragment.gameEntity = gameEntity dialogFragment.callBack = callBack dialogFragment.show(activity.supportFragmentManager, PackageCheckDialogFragment::class.java.name) } else { dialogFragment.gameEntity = gameEntity dialogFragment.callBack = callBack val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction() transaction.show(dialogFragment) transaction.commit() } } private fun checkDetectionsInstalled( allInstalledPackages: List, packages: ArrayList ): Boolean { var isPackagesInstalled = false packages.forEach { packageName -> val isInstalled = allInstalledPackages.find { it == packageName } != null if (isInstalled) { isPackagesInstalled = true return@forEach } } return isPackagesInstalled } fun isAllPackageInstalled( allInstalledPackages: List, packageDialogEntity: PackageDialogEntity ): Boolean { var isAllInstalled = true packageDialogEntity.detectionObjects.forEach loop@{ obj -> if (!checkDetectionsInstalled(allInstalledPackages, obj.packages)) { isAllInstalled = false return isAllInstalled } } return isAllInstalled } } }