Files
assistant-android/app/src/main/java/com/gh/common/dialog/PackageCheckDialogFragment.kt

485 lines
20 KiB
Kotlin

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.PackageUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
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 = PackageUtils.getInstalledPackages(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,
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 = 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<LinkEntity>()
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 = PackageUtils.getInstalledPackages(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 = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
mAdapter?.notifyDataSetChanged()
}
}
inner class PackageCheckAdapter(val context: Context, val entities: ArrayList<DetectionObjectEntity>) :
BaseRecyclerAdapter<RecyclerView.ViewHolder>(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, R.color.text_theme))
} else {
holder.binding.statusTv.text = "未安装"
holder.binding.statusTv.setTextColor(ContextCompat.getColor(context, R.color.secondary_red))
}
holder.binding.statusTv.visibility = View.VISIBLE
} else {
holder.binding.statusTv.visibility = View.GONE
}
}
}
}
class PackageCheckViewHolder(val binding: PackageCheckItemBinding) :
BaseRecyclerViewHolder<DetectionObjectEntity>(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 = PackageUtils.getInstalledPackages(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<PackageInfo>,
packages: ArrayList<String>
): Boolean {
var isPackagesInstalled = false
packages.forEach { packageName ->
val isInstalled = allInstalledPackages.find { it.packageName == packageName } != null
if (isInstalled) {
isPackagesInstalled = true
return@forEach
}
}
return isPackagesInstalled
}
fun isAllPackageInstalled(
allInstalledPackages: List<PackageInfo>,
packageDialogEntity: PackageDialogEntity
): Boolean {
var isAllInstalled = true
packageDialogEntity.detectionObjects.forEach loop@{ obj ->
if (!checkDetectionsInstalled(allInstalledPackages, obj.packages)) {
isAllInstalled = false
return isAllInstalled
}
}
return isAllInstalled
}
}
}