diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f5bd544ea..f4e2ec836d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,7 +72,7 @@ android_build: only: - dev - release - - feat/GHZSCY-6705 + - feat/GHZSCY-5915 # 代码检查 sonarqube_analysis: @@ -157,5 +157,4 @@ oss-upload&send-email: - /usr/local/bin/python /ci-android-mail-jira-comment.py only: - dev - - release - - feat/GHZSCY-6705 \ No newline at end of file + - release \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index 437077eb01..b38ed479a1 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -294,7 +294,7 @@ public class BindingAdapters { }); }); } else { - ReservationHelper.showCancelReservationDialog(progressBar.getContext(), () -> { + ReservationHelper.showCancelReservationDialog(progressBar.getContext(),gameEntity, () -> { ReservationHelper.cancelReservation(gameEntity, () -> { updateReservation(progressBar, gameEntity); }); diff --git a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt index 68ed1ecb9b..84a3f47538 100644 --- a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt +++ b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt @@ -1,27 +1,42 @@ package com.gh.common.dialog +import android.content.Context +import android.content.DialogInterface +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.IExposable +import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.base.fragment.BaseDialogFragment -import com.gh.gamecenter.common.entity.SimpleGameEntity -import com.gh.gamecenter.common.utils.ImageUtils -import com.gh.gamecenter.common.utils.fromHtml +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.SPUtils.getBoolean import com.gh.gamecenter.databinding.DialogReserveBinding import com.gh.gamecenter.databinding.DialogReserveItemBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.ReserveOnlineEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.mygame.MyGameActivity +import com.halo.assistant.HaloApp import com.lightgame.adapter.BaseRecyclerAdapter class ReserveDialog : BaseDialogFragment() { - private lateinit var mReserveList: List + private lateinit var reserveOnlineEntity: ReserveOnlineEntity + val games: List + get() = reserveOnlineEntity.games + private var mDismissListener: (() -> Unit)? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -36,66 +51,162 @@ class ReserveDialog : BaseDialogFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val binding: DialogReserveBinding = DialogReserveBinding.inflate(layoutInflater, null, false) - mReserveList = arguments?.getParcelableArrayList(RESERVE_LIST) ?: arrayListOf() + reserveOnlineEntity = arguments?.getParcelable(RESERVE_ONLINE) ?: ReserveOnlineEntity() - binding.title.text = resources.getString(R.string.dialog_reserve_title, mReserveList.size).fromHtml() - binding.more.visibility = if (mReserveList.size > 4) { - View.VISIBLE - } else View.GONE - binding.more.setOnClickListener { + binding.tvTitle.text = + resources.getString(R.string.dialog_reserve_title, reserveOnlineEntity.gamesTotal).fromHtml() + binding.tvViewAllAppointment.setOnClickListener { + SensorsBridge.trackAppointmentGameOnlineDialogClick(buttonName = "查看全部预约") val intent = MyGameActivity.getIntentWithConfig(requireContext(), MyGameActivity.RESERVATION_INDEX) startActivity(intent) + } + + val adapter = ReserveDialogAdapter(requireContext(), games) + binding.rvGames.layoutManager = FixLinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + binding.rvGames.adapter = adapter + + val exposureListener = ExposureListener(this, adapter) + binding.rvGames.addOnScrollListener(exposureListener) + + binding.ivClose.setOnClickListener { dismissAllowingStateLoss() } - binding.recyclerView.layoutManager = if (mReserveList.size > 4) { - GridLayoutManager(context, 4) - } else { - FixLinearLayoutManager(context, RecyclerView.HORIZONTAL, false) - } - binding.recyclerView.adapter = object : BaseRecyclerAdapter(context) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReserveDialogItemViewHolder { - val inflate = mLayoutInflater.inflate(R.layout.dialog_reserve_item, parent, false) - return ReserveDialogItemViewHolder(DialogReserveItemBinding.bind(inflate)) - } - override fun getItemCount(): Int { - return if (mReserveList.size > 4) 4 else mReserveList.size - } - - override fun onBindViewHolder(holder: ReserveDialogItemViewHolder, position: Int) { - val entity = mReserveList[position] - ImageUtils.display(holder.binding.icon, entity.icon) - holder.binding.gameNameTv.text = entity.name - holder.itemView.setOnClickListener { - GameDetailActivity.startGameDetailActivity(mContext, entity.id, "(预约弹窗)") - dismissAllowingStateLoss() - } - } - } + checkHasAutoDownload(binding) dialog?.setCanceledOnTouchOutside(true) return binding.root } + private fun checkHasAutoDownload(binding: DialogReserveBinding) { + if (reserveOnlineEntity.wifiAutoDownloadTotal > 0) { + // 开启自动下载 + binding.tvAutoDownloadTips.goneIf(false) + var firstAutoDownloadGameName = + reserveOnlineEntity.games.find { it.wifiAutoDownload && !it.isLandPageAddressDialog() }?.name + + if(!firstAutoDownloadGameName.isNullOrBlank() && firstAutoDownloadGameName.length > GAME_NAME_SHOW_MAX_LENGTH){ + firstAutoDownloadGameName = "${firstAutoDownloadGameName.take(GAME_NAME_SHOW_MAX_LENGTH-1)}..." + } + val isWifiOpen = NetworkUtils.isWifiConnected(HaloApp.getInstance()) + binding.tvAutoDownloadTips.text = + if (isWifiOpen) { + if (firstAutoDownloadGameName.isNullOrBlank()) { + getString( + R.string.reserve_reminder_auto_download, + "${reserveOnlineEntity.wifiAutoDownloadTotal}" + ) + } else { + getString( + R.string.reserve_reminder_auto_download_with_name, + firstAutoDownloadGameName, + "${reserveOnlineEntity.wifiAutoDownloadTotal}" + ) + } + } else { + if (firstAutoDownloadGameName.isNullOrBlank()) { + getString( + R.string.reserve_reminder_wait_for_wifi_to_auto_download, + "${reserveOnlineEntity.wifiAutoDownloadTotal}" + ) + } else { + getString( + R.string.reserve_reminder_wait_for_wifi_to_auto_download_with_name, + firstAutoDownloadGameName, + "${reserveOnlineEntity.wifiAutoDownloadTotal}" + ) + } + } + + binding.tvAutoDownloadTips.setOnClickListener { + SensorsBridge.trackAppointmentGameOnlineDialogClick(buttonName = "查看进度") + val intent = DownloadManagerActivity.getDownloadMangerIntent(requireContext(), "") + startActivity(intent) + } + } else { + binding.tvAutoDownloadTips.goneIf(true) + } + } + fun setOnDismissListener(dismissListener: () -> Unit) { mDismissListener = dismissListener } + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + SensorsBridge.trackAppointmentGameOnlineDialogClick(buttonName = "关闭弹窗") + } + + override fun onStart() { + super.onStart() + dialog?.window?.let { + it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + val params = it.attributes + params.width = DisplayUtils.dip2px(300F) + it.attributes = params + } + } + override fun onDestroy() { super.onDestroy() mDismissListener?.invoke() } companion object { - const val RESERVE_LIST = "reserve_list" + const val RESERVE_ONLINE = "reserve_online" + private const val GAME_NAME_SHOW_MAX_LENGTH = 8 @JvmStatic - fun getInstance(reserveList: List) = ReserveDialog().apply { - arguments = Bundle() - arguments?.putParcelableArrayList(RESERVE_LIST, ArrayList(reserveList)) + fun getInstance(reserveOnlineEntity: ReserveOnlineEntity?) = ReserveDialog().apply { + arguments = Bundle().apply { + putParcelable(RESERVE_ONLINE, reserveOnlineEntity) + } } } } +class ReserveDialogAdapter( + context: Context, + private val games: List +) : + BaseRecyclerAdapter(context), IExposable { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReserveDialogItemViewHolder { + return ReserveDialogItemViewHolder(parent.toBinding()) + } + + override fun getItemCount(): Int { + return if (games.size > 8) 8 else games.size + } + + override fun onBindViewHolder(holder: ReserveDialogItemViewHolder, position: Int) { + val entity = games[position] + holder.binding.icon.displayGameIcon(entity) + holder.binding.gameNameTv.text = entity.name + holder.itemView.setOnClickListener { + SensorsBridge.trackAppointmentGameOnlineDialogClick( + "游戏", + entity.id, + entity.name ?: "", + entity.categoryChinese, + ) + GameDetailActivity.startGameDetailActivity(mContext, entity.id, "(预约弹窗)", entity.exposureEvent) + } + entity.exposureEvent = ExposureEvent.createEvent( + entity, + listOf(ExposureSource("预约上线弹窗", "")) + ) + } + + override fun getEventByPosition(pos: Int): ExposureEvent? { + return games.getOrNull(pos)?.exposureEvent + } + + override fun getEventListByPosition(pos: Int): List? { + return null + } + +} + class ReserveDialogItemViewHolder(val binding: DialogReserveItemBinding) : BaseRecyclerViewHolder(binding.root) \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/dialog/ReserveReminderPhoneNumberDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveReminderPhoneNumberDialog.kt new file mode 100644 index 0000000000..0efc65a532 --- /dev/null +++ b/app/src/main/java/com/gh/common/dialog/ReserveReminderPhoneNumberDialog.kt @@ -0,0 +1,278 @@ +package com.gh.common.dialog + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.text.InputFilter +import android.view.LayoutInflater +import androidx.core.widget.addTextChangedListener +import com.gh.gamecenter.R +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.toResString +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.DialogReserveReminderPhoneNumberBinding +import com.gh.gamecenter.entity.ReserveModifyEntity +import com.gh.gamecenter.entity.ValidateCodeResponse +import com.halo.assistant.fragment.reserve.OnReserveReminderListener +import com.halo.assistant.fragment.reserve.ReserveReminderRepository +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import org.json.JSONObject +import retrofit2.HttpException +import java.util.concurrent.TimeUnit + + +class ReserveReminderPhoneNumberDialog( + context: Context, + themeResId: Int, + private val dialogType: Int, + private val phoneNumber: String, + private var serviceId: String, + private val gameId: String, + private val repository: ReserveReminderRepository, + private val listener: OnReserveReminderListener +) : + Dialog(context, themeResId) { + + private val compositeDisposable = CompositeDisposable() + + private lateinit var binding: DialogReserveReminderPhoneNumberBinding + + private var hasValidated = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = DialogReserveReminderPhoneNumberBinding.inflate(LayoutInflater.from(context)) + setContentView(binding.root) + initView() + } + + private fun initView() { + binding.vClose.setOnClickListener { + dismiss() + } + + setSubmitState(phoneNumber) + binding.etInput.addTextChangedListener { + val content = binding.etInput.text?.toString() ?: "" + if (dialogType == DIALOG_TYPE_BIND_PHONE || dialogType == DIALOG_TYPE_CHANGE_PHONE) { + setSubmitState(content) + } else { + val length = content.length + if (length == VALIDATE_CODE_LENGTH) { + verifyPhoneNumber(content) + } + } + + } + + when (dialogType) { + DIALOG_TYPE_BIND_PHONE -> { + binding.tvTitle.setText(R.string.enable_sms_reminder) + binding.tvDescription.setText(R.string.enable_sms_reminder_description) + binding.etInput.setText(phoneNumber) + binding.etInput.setHint(com.gh.gamecenter.login.R.string.input_phone_hint) + } + + DIALOG_TYPE_CHANGE_PHONE -> { + binding.tvTitle.setText(R.string.change_phone_number_2) + binding.tvDescription.setText(R.string.enable_sms_reminder_description) + binding.etInput.setText(phoneNumber) + binding.etInput.setHint(com.gh.gamecenter.login.R.string.input_phone_hint) + } + + DIALOG_TYPE_VERIFY_PHONE -> { + startCountDown() + binding.tvTitle.setText(R.string.please_input_sms_verify_code) + if (phoneNumber.length == PHONE_NUMBER_LENGTH) { + val phoneNumberWithMask = phoneNumber.substring(0, 3) + MASK + phoneNumber.substring(7) + binding.tvDescription.text = + context.getString(R.string.verify_code_send_with_phone_number, phoneNumberWithMask) + } + binding.tvSubmit.alpha = 1.0F + binding.etInput.filters = arrayOf(InputFilter.LengthFilter(VALIDATE_CODE_LENGTH)) + binding.etInput.setHint(R.string.please_input_verify_code) + + } + } + binding.tvSubmit.setOnClickListener { + val text = binding.etInput.text?.toString() ?: "" + if (dialogType == DIALOG_TYPE_VERIFY_PHONE) { + resendValidateCode() + } else { + if (isEnableSubmit(text)) { + when (dialogType) { + DIALOG_TYPE_BIND_PHONE -> bindPhoneNumber(text) + DIALOG_TYPE_CHANGE_PHONE -> bindPhoneNumber(text) + } + } else { + if (text != phoneNumber) { + ToastUtils.toast(R.string.invalid_phone_number_format.toResString()) + } + } + + } + + } + } + + + private fun resendValidateCode() { + repository.sendVerifyCode(phoneNumber) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ValidateCodeResponse) { + serviceId = data.serviceId + startCountDown() + } + + }).let(compositeDisposable::add) + } + + private fun verifyPhoneNumber(content: String) { + repository.verifyPhoneNumber(phoneNumber, content, serviceId) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + hasValidated = true + listener.phoneNumberValidateSuccessfully() + ToastUtils.toast(R.string.phone_number_validate_successfully.toResString()) + dismiss() + + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + binding.etInput.text = null + ToastUtils.toast(R.string.phone_number_validate_failure.toResString()) + } + }).let(compositeDisposable::add) + } + + + private fun bindPhoneNumber(phone: String) { + repository.bindPhone(gameId, phone) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ReserveModifyEntity) { + listener.bindPhoneSuccessfully(data.smsConfig.mobileValidated, phone) + dismiss() + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + if (exception is HttpException) { + try { + val string = exception.response().errorBody()?.string() + if (!string.isNullOrBlank()) { + val json = JSONObject(string) + val message = json.getJSONObject("toast").getString("sms_config.mobile") + ToastUtils.showToast(message) + } + } catch (e: Exception) { + // no implement + } + } + + } + }).let(compositeDisposable::add) + } + + + private fun startCountDown() { + binding.tvSubmit.isEnabled = false + binding.tvSubmit.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_gray) + binding.tvSubmit.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context)) + Observable.interval(0, 1, TimeUnit.SECONDS) + .take(COUNT_DOWN_DURATION) + .map { + COUNT_DOWN_DURATION - it + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onSubscribe(d: Disposable) { + compositeDisposable.add(d) + } + + + override fun onNext(time: Long) { + binding.tvSubmit.text = context.getString(R.string.resend_with_time, "$time") + } + + override fun onComplete() { + binding.tvSubmit.isEnabled = true + binding.tvSubmit.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_fill_blue) + binding.tvSubmit.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context)) + binding.tvSubmit.setText(R.string.resend) + + } + }) + } + + override fun dismiss() { + compositeDisposable.clear() + if (dialogType == DIALOG_TYPE_VERIFY_PHONE && !hasValidated) { + ToastUtils.toast(R.string.phone_number_validate_cancel.toResString()) + } + super.dismiss() + } + + private fun isEnableSubmit(phone: String) = + PHONE_NUMBER_PATTERN.toRegex().matches(phone) + && if (dialogType == DIALOG_TYPE_CHANGE_PHONE) phone != phoneNumber else true + + private fun setSubmitState(phone: String) { + val isEnable = isEnableSubmit(phone) + binding.tvSubmit.alpha = if (isEnable) { + TV_SUBMIT_ENABLE_ALPHA + } else { + TV_SUBMIT_UNABLE_ALPHA + } + } + + companion object { + + private const val PHONE_NUMBER_PATTERN = "^1\\d{10}$" + private const val MASK = "****" + private const val PHONE_NUMBER_LENGTH = 11 + private const val VALIDATE_CODE_LENGTH = 6 + + const val DIALOG_TYPE_BIND_PHONE = 0 + const val DIALOG_TYPE_CHANGE_PHONE = 1 + const val DIALOG_TYPE_VERIFY_PHONE = 2 + + private const val COUNT_DOWN_DURATION = 60L + + private const val TV_SUBMIT_ENABLE_ALPHA = 1F + private const val TV_SUBMIT_UNABLE_ALPHA = 0.4F + + fun create( + context: Context, + dialogType: Int, + phoneNumber: String, + serviceId: String, + gameId: String, + repository: ReserveReminderRepository, + listener: OnReserveReminderListener + ) = + ReserveReminderPhoneNumberDialog( + context, + com.gh.gamecenter.common.R.style.DialogWindowTransparent, + dialogType, + phoneNumber, + serviceId, + gameId, + repository, + listener + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/dialog/ReserveSuccessReminderDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveSuccessReminderDialog.kt new file mode 100644 index 0000000000..5547bdfde8 --- /dev/null +++ b/app/src/main/java/com/gh/common/dialog/ReserveSuccessReminderDialog.kt @@ -0,0 +1,428 @@ +package com.gh.common.dialog + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.TextUtils +import android.view.* +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.view.updateLayoutParams +import com.gh.common.pop.EditBindPhoneInfoPop +import com.gh.common.pop.EditBindWechatPop +import com.gh.common.pop.RealNameTipsPop +import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toObject +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.* +import com.gh.gamecenter.entity.ReserveReminderEntity +import com.gh.gamecenter.login.entity.UserInfoEntity +import com.gh.gamecenter.login.user.UserManager +import com.halo.assistant.HaloApp +import com.halo.assistant.fragment.reserve.OnReserveReminderListener + +class ReserveSuccessReminderDialog( + context: Context, + themeResId: Int, + private val listener: OnReserveReminderListener +) : + Dialog(context, themeResId), OnReserveSuccessListener { + + private var reserveReminder = ReserveReminderEntity() + + private val handler = Handler(Looper.getMainLooper()) + + private val handlers = arrayListOf() + + private lateinit var binding: DialogReserveSuccessWithSmsBinding + + private val realNameQuestionPop by lazy { + RealNameTipsPop.create(context, listener) + } + + private var hasAutoPopped = false + private var isInit = true + + override fun onTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_UP) { + if (realNameQuestionPop.isShowing) { + realNameQuestionPop.dismiss() + return true + } else if (handlers.any { it.hasPopShowing }) { + handlers.forEach { + it.dismissPop() + } + return true + } + } + return super.onTouchEvent(event) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DialogReserveSuccessWithSmsBinding.inflate(LayoutInflater.from(context)) + setContentView(binding.root) + + binding.ivClose.setOnClickListener { + dismiss() + } + binding.ivQuestion.setOnClickListener { + realNameQuestionPop.showAtLocation(binding.cbAutoDownload, Gravity.BOTTOM, 0, 4F.dip2px()) + } + + } + + private fun initView() { + handlers.clear() + binding.flContentContainer.removeAllViews() + + val smsConfig = reserveReminder.smsConfig + val wechatConfig = reserveReminder.wechatConfig + when { + reserveReminder.onlyShowWechatReminder && !wechatConfig.isReminderEnable -> { // 只显示微信:未开启 + binding.tvContent.setText(R.string.reverse_success_without_reminder_tips) + arrayListOf(OnlyWechatReminderUnableHandler(16.dp, binding.flContentContainer, this)) + } + + + reserveReminder.onlyShowWechatReminder && wechatConfig.isReminderEnable -> { // 只显示微信:已开 + binding.tvContent.setText(R.string.reverse_success_with_reminder_tips) + arrayListOf( + WechatReminderEnableHandler( + reserveReminder.wechatConfig.nickName, + 8.dp, + binding.flContentContainer, + this + ) + ) + } + + !smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信,微信未开启 + binding.tvContent.setText(R.string.reverse_success_without_reminder_tips) + arrayListOf( + SmsReminderUnableHandler(16.dp, binding.flContentContainer, this), + WechatReminderUnableHandler(8.dp, binding.flContentContainer, this) + ) + } + + smsConfig.notice && wechatConfig.isReminderEnable -> {// 短信,微信已开启 + binding.tvContent.setText(R.string.reverse_success_with_reminder_tips) + arrayListOf( + SmsReminderEnableHandler(reserveReminder.smsConfig, 8.dp, binding.flContentContainer, this), + WechatReminderEnableHandler( + reserveReminder.wechatConfig.nickName, + 8.dp, + binding.flContentContainer, + this + ) + ) + } + + + smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信开启,微信未开启 + binding.tvContent.setText(R.string.reverse_success_with_reminder_tips) + arrayListOf( + SmsReminderEnableHandler(smsConfig, 8.dp, binding.flContentContainer, this), + WechatReminderUnableHandler(16.dp, binding.flContentContainer, this) + ) + } + + + !smsConfig.notice && wechatConfig.isReminderEnable -> { // 微信开启,短信未开启 + binding.tvContent.setText(R.string.reverse_success_with_reminder_tips) + arrayListOf( + WechatReminderEnableHandler( + wechatConfig.nickName, + 8.dp, + binding.flContentContainer, + this + ), + SmsReminderUnableHandler(16.dp, binding.flContentContainer, this) + ) + } + + else -> { + binding.tvContent.setText(R.string.reverse_success_without_reminder_tips) + arrayListOf() + } + }.let { + handlers.clear() + handlers.addAll(it) + } + handlers.forEach { + binding.flContentContainer.addView(it.init()) + } + + if (isInit) { + isInit = false + binding.gAutoDownload.goneIf(!reserveReminder.isEnableAutoDownload) + if (reserveReminder.isEnableAutoDownload) { + binding.cbAutoDownload.isChecked = reserveReminder.wifiAutoDownload + checkIfShowRealNamePop() + } + binding.cbAutoDownload.setOnCheckedChangeListener { _, isChecked -> + listener.enableAutoDownload(isChecked) + checkIfShowRealNamePop() + } + } + + } + + private fun checkIfShowRealNamePop() { + if (hasAutoPopped) { + return + } + hasAutoPopped = true + handler.postDelayed({ + val idCard = UserManager.getInstance().userInfoEntity?.idCard + if (idCard != null && idCard.status != 1 && idCard.minor != true) { + // 账号已实名 + return@postDelayed + } + + val deviceCertificationInfoString = + SPUtils.getString(Constants.SP_DEVICE_CERTIFICATION_PREFIX + HaloApp.getInstance().gid) + if (!TextUtils.isEmpty(deviceCertificationInfoString)) { + val deviceIdCard = deviceCertificationInfoString.toObject()?.idCard + if (deviceIdCard != null && deviceIdCard.status != 1 && deviceIdCard.minor != true) { + // 设备已实名 + return@postDelayed + } + } + realNameQuestionPop.showAtLocation(binding.cbAutoDownload, Gravity.BOTTOM, 0, 0) + }, 16) + } + + override fun updateSmsReminder(isEnable: Boolean) { + listener.updateSmsReminder(isEnable) + } + + override fun updateWechatReminder() { + listener.updateWechatReminder() + } + + override fun changePhoneNumber() { + listener.changedPhoneNumber() + } + + override fun verifyPhoneNumber() { + listener.verifyPhoneNumber() + } + + override fun changeWechatBinding() { + listener.changeWechatBinding() + } + + override fun onStart() { + super.onStart() + window?.let { + it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + val params = it.attributes + params.width = DisplayUtils.dip2px(300F) + it.attributes = params + } + } + + fun updateReserveReminder(reserveReminder: ReserveReminderEntity) { + this.reserveReminder = reserveReminder + initView() + } + + override fun dismiss() { + handler.removeCallbacksAndMessages(null) + super.dismiss() + } + + companion object { + private val Int.dp: Int + get() = DisplayUtils.dip2px(this.toFloat()) + + fun create(context: Context, listener: OnReserveReminderListener) = + ReserveSuccessReminderDialog( + context, + com.gh.gamecenter.common.R.style.DialogWindowTransparent, + listener + ).apply { + + } + } + + abstract class ReminderContentHandler( + val topMargin: Int, + val parent: ViewGroup, + val listener: OnReserveSuccessListener + ) { + + open val hasPopShowing: Boolean = false + + fun init(): View { + val root = createView(LayoutInflater.from(parent.context)) + root.updateLayoutParams { + this.topMargin = this@ReminderContentHandler.topMargin + } + initView() + return root + } + + protected abstract fun createView(inflater: LayoutInflater): View + + protected abstract fun initView() + + open fun dismissPop() = Unit + } + + class SmsReminderUnableHandler(topMargin: Int, parent: ViewGroup, listener: OnReserveSuccessListener) : + ReminderContentHandler(topMargin, parent, listener) { + + private lateinit var binding: LayoutReserveSmsReminderUnableBinding + + override fun createView(inflater: LayoutInflater): View { + return LayoutReserveSmsReminderUnableBinding.inflate(inflater, parent, false) + .also { + binding = it + }.root + } + + override fun initView() { + binding.vSmsAdd.setOnClickListener { + listener.updateSmsReminder(true) + } + } + + } + + class WechatReminderUnableHandler(topMargin: Int, parent: ViewGroup, listener: OnReserveSuccessListener) : + ReminderContentHandler(topMargin, parent, listener) { + + private lateinit var binding: LayoutReserveWechatReminderUnableBinding + + override fun createView(inflater: LayoutInflater): View { + return LayoutReserveWechatReminderUnableBinding.inflate(inflater, parent, false) + .also { + binding = it + }.root + } + + override fun initView() { + binding.vWechatAdd.setOnClickListener { + listener.updateWechatReminder() + } + } + + } + + class SmsReminderEnableHandler( + private val smsConfig: ReserveReminderEntity.SmsConfig, + topMargin: Int, + parent: ViewGroup, listener: + OnReserveSuccessListener + ) : + ReminderContentHandler(topMargin, parent, listener) { + + private lateinit var binding: LayoutReserveSmsReminderEnableBinding + + override val hasPopShowing: Boolean + get() = editBindPhoneInfoPop.isShowing + + override fun dismissPop() { + if (hasPopShowing) { + editBindPhoneInfoPop.dismiss() + } + } + + private val editBindPhoneInfoPop by lazy { + EditBindPhoneInfoPop.create(parent.context, smsConfig.mobileValidated, listener) + } + + override fun createView(inflater: LayoutInflater) = + LayoutReserveSmsReminderEnableBinding.inflate(inflater, parent, false) + .also { + binding = it + }.root + + override fun initView() { + binding.tvPhone.text = smsConfig.mobile + binding.vSmsEdit.setOnClickListener { + editBindPhoneInfoPop.showAsDropDown( + binding.ivSmsEdit, + (-104).dp, + 0, + ) + } + } + } + + class WechatReminderEnableHandler( + private val nickName: String, + topMargin: Int, + parent: ViewGroup, + listener: OnReserveSuccessListener + ) : + ReminderContentHandler(topMargin, parent, listener) { + + private lateinit var binding: LayoutReserveWechatReminderEnableBinding + + private val editWechatPop by lazy { + EditBindWechatPop.create(parent.context, listener) + } + + override fun createView(inflater: LayoutInflater) = + LayoutReserveWechatReminderEnableBinding.inflate(inflater, parent, false) + .also { + binding = it + }.root + + override fun initView() { + binding.tvWechat.text = nickName + binding.vModifyWechat.setOnClickListener { + editWechatPop.showAsDropDown( + binding.ivModifyWechat, + (-104).dp, + 0 + ) + } + } + + } + + class OnlyWechatReminderUnableHandler(topMargin: Int, parent: ViewGroup, listener: OnReserveSuccessListener) : + ReminderContentHandler(topMargin, parent, listener) { + + private lateinit var binding: LayoutReserveOnlyWechatUnableBinding + + override fun createView(inflater: LayoutInflater) = + LayoutReserveOnlyWechatUnableBinding.inflate(inflater, parent, false) + .also { + binding = it + }.root + + override fun initView() { + binding.vSubmitBackground.setOnClickListener { + listener.updateWechatReminder() + } + } + + } + + +} + +interface OnReserveSuccessListener { + + fun updateSmsReminder(isEnable: Boolean) + + fun updateWechatReminder() + + fun changePhoneNumber() + + fun verifyPhoneNumber() + + fun changeWechatBinding() +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/dialog/WechatBindingConflictDialogFragment.kt b/app/src/main/java/com/gh/common/dialog/WechatBindingConflictDialogFragment.kt new file mode 100644 index 0000000000..5860da079c --- /dev/null +++ b/app/src/main/java/com/gh/common/dialog/WechatBindingConflictDialogFragment.kt @@ -0,0 +1,63 @@ +package com.gh.common.dialog + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.DialogWechatBindingConflictBinding + +class WechatBindingConflictDialogFragment : BaseDialogFragment() { + + private lateinit var binding: DialogWechatBindingConflictBinding + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + setCanceledOnTouchOutside(true) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return DialogWechatBindingConflictBinding.inflate(inflater, container, false) + .also { + binding = it + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.tvIKnow.setOnClickListener { + dismiss() + } + } + + override fun onStart() { + super.onStart() + dialog?.window?.let { + it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + val params = it.attributes + params.width = DisplayUtils.dip2px(300F) + it.attributes = params + } + } + + companion object { + + fun show(context: Context) { + if (context is AppCompatActivity) { + context.supportFragmentManager + } else { + (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager + }?.let { + WechatBindingConflictDialogFragment().show(it, WechatBindingConflictDialogFragment::class.java.name) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/dialog/WechatBindingFailedDialogFragment.kt b/app/src/main/java/com/gh/common/dialog/WechatBindingFailedDialogFragment.kt new file mode 100644 index 0000000000..fa914437da --- /dev/null +++ b/app/src/main/java/com/gh/common/dialog/WechatBindingFailedDialogFragment.kt @@ -0,0 +1,125 @@ +package com.gh.common.dialog + +import android.app.Dialog +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.entity.ErrorEntity +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.DialogWechatBindingFailedBinding +import com.gh.gamecenter.login.user.UserRepository +import com.lightgame.utils.Utils + +class WechatBindingFailedDialogFragment : BaseDialogFragment() { + + private lateinit var binding: DialogWechatBindingFailedBinding + + private var currentUserId: String? = null + private lateinit var userConflict: ErrorEntity.Data.UserConflict + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + userConflict = arguments?.getParcelable(KEY_USER_CONFLICT) ?: ErrorEntity.Data.UserConflict() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + setCanceledOnTouchOutside(true) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return DialogWechatBindingFailedBinding.inflate(inflater, container, false) + .also { + binding = it + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + UserRepository.getInstance().loginUserInfo.observe(viewLifecycleOwner) { + currentUserId = it.data.getShortUserId() + binding.tvCurrentName.text = it.data.name + binding.ivCurrentAvatar.displayAvatar(it.data.icon) + binding.tvUserId.text = getString(R.string.user_id, currentUserId) + } + + binding.tvConflictName.text = userConflict.name + binding.ivConflictAvatar.displayAvatar(userConflict.icon) + binding.tvConflictUserId.text = getString(R.string.user_id, userConflict.seq) + + + binding.tvConflictProblem.setOnClickListener { + dismiss() + WechatBindingConflictDialogFragment.show(requireContext()) + } + + binding.tvIKnow.setOnClickListener { + dismiss() + } + + binding.tvUserId.setOnLongClickListener { + currentUserId?.let { + copyTextToClipboard(it) + true + } ?: false + } + + binding.tvConflictUserId.setOnLongClickListener { + copyTextToClipboard(userConflict.id) + true + } + } + + private fun copyTextToClipboard(text: String) { + val clipboardManager = context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager + if (clipboardManager != null) { + val clip = ClipData.newPlainText(COPY_ID_TEXT, text) + clipboardManager.setPrimaryClip(clip) + Utils.toast(requireContext(), R.string.copy_user_id_successfully) + } + + } + + override fun onStart() { + super.onStart() + dialog?.window?.let { + it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + val params = it.attributes + params.width = DisplayUtils.dip2px(300F) + it.attributes = params + } + } + + companion object { + + private const val COPY_ID_TEXT = "user_id" + private const val KEY_USER_CONFLICT = "key_user_conflict" + + fun show(context: Context, userConflict: ErrorEntity.Data.UserConflict?) { + if (context is AppCompatActivity) { + context.supportFragmentManager + } else { + (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager + }?.let { + val fragment = WechatBindingFailedDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(KEY_USER_CONFLICT, userConflict) + } + } + fragment.show(it, WechatBindingFailedDialogFragment::class.java.name) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/BatchManagementPop.kt b/app/src/main/java/com/gh/common/pop/BatchManagementPop.kt new file mode 100644 index 0000000000..1145fe4601 --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/BatchManagementPop.kt @@ -0,0 +1,44 @@ +package com.gh.common.pop + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import com.gh.gamecenter.R +import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding +import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.LayoutAddKaifuPopupItemBinding + +class BatchManagementPop( + binding: LayoutPopupContainerBinding, + private val listener: OnBatchManagementListener +) : + CommonPopupWindow(binding) { + override val viewDataList: List + get() = listOf( + ViewData(R.string.enable_automatic_downloading_in_batches) { + listener.enableAutoDownload() + }, + ViewData(R.string.batch_management) { + listener.batchManage() + } + ) + + companion object { + + fun create(context: Context, listener: OnBatchManagementListener): BatchManagementPop { + val binding = LayoutPopupContainerBinding.inflate(LayoutInflater.from(context)) + return BatchManagementPop(binding, listener).apply { + initView() + } + } + } + + interface OnBatchManagementListener { + + fun enableAutoDownload() + + fun batchManage() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/CancelReservePop.kt b/app/src/main/java/com/gh/common/pop/CancelReservePop.kt new file mode 100644 index 0000000000..839e1a7b67 --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/CancelReservePop.kt @@ -0,0 +1,49 @@ +package com.gh.common.pop + +import android.content.Context +import android.view.LayoutInflater +import com.gh.gamecenter.R +import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding +import com.gh.gamecenter.feature.entity.GameEntity + +class CancelReservePop( + private val game: GameEntity, + binding: LayoutPopupContainerBinding, + private val listener: OnCancelReserveListener + +) : CommonPopupWindow(binding) { + + override val viewDataList: List + get() { + val list = arrayListOf() + if (game.wifiAutoDownloadEnable) { + list.add(ViewData(if (game.wifiAutoDownload) R.string.cancel_auto_download_with_wifi else R.string.enable_automatic_downloading_with_wifi) { + listener.enableAutoDownload(!game.wifiAutoDownload) + }) + } + list.add(ViewData(R.string.cancel_reserve, listener::cancelReserve)) + return list + } + + companion object { + + fun create( + game: GameEntity, + context: Context, + listener: OnCancelReserveListener + ): CancelReservePop { + val inflater = LayoutInflater.from(context) + val binding = LayoutPopupContainerBinding.inflate(inflater) + return CancelReservePop(game, binding, listener).apply { + initView() + } + } + } + + interface OnCancelReserveListener { + + fun enableAutoDownload(isEnable: Boolean) + + fun cancelReserve() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/CommonPopupWindow.kt b/app/src/main/java/com/gh/common/pop/CommonPopupWindow.kt new file mode 100644 index 0000000000..8cb1f1aaee --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/CommonPopupWindow.kt @@ -0,0 +1,47 @@ +package com.gh.common.pop + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding +import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.LayoutAddKaifuPopupBinding +import com.gh.gamecenter.databinding.LayoutAddKaifuPopupItemBinding +import com.gh.gamecenter.databinding.LayoutPopupOptionItemBinding +import com.gh.gamecenter.databinding.LayoutPopupReserveReminderOptionItemBinding + +abstract class CommonPopupWindow( + private val binding: LayoutPopupContainerBinding, +) : BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) { + + private val inflater = LayoutInflater.from(binding.root.context) + + abstract val viewDataList: List + + fun initView() { + isFocusable = true + binding.container.updateLayoutParams { + width = DisplayUtils.dip2px(128F) + } + viewDataList.forEach { + addItemView(it.textResId, it.click) + } + } + + private fun addItemView(textResId: Int, click: () -> Unit) = + LayoutPopupReserveReminderOptionItemBinding.inflate(inflater, binding.container, false) + .also { + it.hintText.setText(textResId) + binding.container.addView(it.root) + it.root.setOnClickListener { + dismiss() + click() + } + }.root + + data class ViewData( + val textResId: Int, + val click: () -> Unit + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/EditBindPhoneInfoPop.kt b/app/src/main/java/com/gh/common/pop/EditBindPhoneInfoPop.kt new file mode 100644 index 0000000000..c5d9882d73 --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/EditBindPhoneInfoPop.kt @@ -0,0 +1,70 @@ +package com.gh.common.pop + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import com.gh.common.dialog.OnReserveSuccessListener +import com.gh.gamecenter.R +import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding +import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.LayoutAddKaifuPopupItemBinding + +class EditBindPhoneInfoPop( + private val hasValidated: Boolean, + private val binding: LayoutPopupContainerBinding, + private val listener: OnReserveSuccessListener +) : BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) { + + private val context + get() = binding.root.context + + init { + initView() + } + + private fun initView() { + isOutsideTouchable = false + isFocusable = true + binding.container.updateLayoutParams { + width = DisplayUtils.dip2px(128F) + } + val inflater = LayoutInflater.from(context) + fun createItemView(textResId: Int) = + LayoutAddKaifuPopupItemBinding.inflate(inflater, binding.container, false) + .also { + it.hintText.setText(textResId) + binding.container.addView(it.root) + }.root + + val tvChangePhoneNumber = createItemView(R.string.change_phone_number) + if(!hasValidated){ + val tvVerifyPhoneNumber = createItemView(R.string.verify_phone_number) + tvVerifyPhoneNumber.setOnClickListener { + dismiss() + listener.verifyPhoneNumber() + } + } + + val tvTurnOffSmsReminder = createItemView(R.string.turn_off_sms_reminders) + + tvChangePhoneNumber.setOnClickListener { + dismiss() + listener.changePhoneNumber() + } + + tvTurnOffSmsReminder.setOnClickListener { + dismiss() + listener.updateSmsReminder(false) + } + } + + companion object { + + fun create(context: Context, hasValidated: Boolean, listener: OnReserveSuccessListener): EditBindPhoneInfoPop { + val binding = LayoutPopupContainerBinding.inflate(LayoutInflater.from(context)) + return EditBindPhoneInfoPop(hasValidated, binding, listener) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/EditBindWechatPop.kt b/app/src/main/java/com/gh/common/pop/EditBindWechatPop.kt new file mode 100644 index 0000000000..e046e5d87c --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/EditBindWechatPop.kt @@ -0,0 +1,26 @@ +package com.gh.common.pop + +import android.content.Context +import android.view.LayoutInflater +import com.gh.common.dialog.OnReserveSuccessListener +import com.gh.gamecenter.R +import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding + +class EditBindWechatPop( + binding: LayoutPopupContainerBinding, + private val listener: OnReserveSuccessListener +) : CommonPopupWindow(binding) { + + override val viewDataList: List + get() = listOf( + ViewData(R.string.change_the_wechat_binding, listener::changeWechatBinding) + ) + + companion object { + + fun create(context: Context, listener: OnReserveSuccessListener): EditBindWechatPop { + val inflater = LayoutInflater.from(context) + return EditBindWechatPop(LayoutPopupContainerBinding.inflate(inflater), listener).apply { initView() } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/RealNameTipsPop.kt b/app/src/main/java/com/gh/common/pop/RealNameTipsPop.kt new file mode 100644 index 0000000000..74415a0fe8 --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/RealNameTipsPop.kt @@ -0,0 +1,69 @@ +package com.gh.common.pop + +import android.content.Context +import android.graphics.Color +import android.text.SpannableString +import android.text.TextPaint +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.core.text.set +import com.gh.common.dialog.OnReserveSuccessListener +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.toResString +import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.PopRealNameTipsBinding +import com.halo.assistant.fragment.reserve.OnReserveReminderListener + +class RealNameTipsPop( + private val binding: PopRealNameTipsBinding, + private val listener: OnReserveReminderListener +) : + BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) { + + private val context: Context + get() = binding.root.context + + init { + initView() + } + + private fun initView() { + + val text = R.string.reserve_real_name_description.toResString() + val spannableString = SpannableString(text) + val clickSpan = object : ClickableSpan() { + override fun updateDrawState(ds: TextPaint) { + ds.bgColor = Color.TRANSPARENT + ds.setColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context)) + } + + override fun onClick(widget: View) { + dismiss() + listener.realName() + } + } + spannableString.set(start = text.length - 6, end = text.length, span = clickSpan) + binding.tvRealNameDescription.text = spannableString + + binding.tvRealNameDescription.movementMethod = LinkMovementMethod.getInstance() + binding.tvRealNameDescription.setHighlightColor(Color.TRANSPARENT) + } + + companion object { + + @JvmStatic + fun create(context: Context, listener: OnReserveReminderListener): RealNameTipsPop { + val pop = RealNameTipsPop(PopRealNameTipsBinding.inflate(LayoutInflater.from(context)), listener) + pop.isFocusable = true + pop.isOutsideTouchable = false + return pop + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/pop/ReserveAllSelectPop.kt b/app/src/main/java/com/gh/common/pop/ReserveAllSelectPop.kt new file mode 100644 index 0000000000..846c64279f --- /dev/null +++ b/app/src/main/java/com/gh/common/pop/ReserveAllSelectPop.kt @@ -0,0 +1,95 @@ +package com.gh.common.pop + +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CompoundButton +import androidx.appcompat.app.AppCompatActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.databinding.PopReserveAllSelectBinding +import com.gh.gamecenter.mygame.MyReservationFragment + +class ReserveAllSelectPop( + val binding: PopReserveAllSelectBinding, + private val listener: OnReserveAllSelectListener +) : BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.MATCH_PARENT, 56F.dip2px()) { + + private var pageStatus: MyReservationFragment.ReservePageState = + MyReservationFragment.ReservePageState.EnableAutoDownload + + private val onCheckedChangeListener = { _: CompoundButton, isChecked: Boolean -> + val count = listener.selectAll(isChecked) + updateSelectedCount(count) + } + + init { + isOutsideTouchable = false + binding.cbAll.setOnCheckedChangeListener(onCheckedChangeListener) + binding.tvSubmit.setOnClickListener { + listener.submit() + } + } + + fun show(state: MyReservationFragment.ReservePageState, context: Context) { + enableSubmit(false) + binding.tvSubmit.setText( + if (state == MyReservationFragment.ReservePageState.EnableAutoDownload) { + R.string.enable_automatic_downloading_with_wifi + } else { + R.string.cancel_reserve + } + ) + + // 清空上一次的状态 + pageStatus = state + updateSelectedCount(0) + + if (!isShowing) { + showAtLocation((context as AppCompatActivity).window.decorView, Gravity.BOTTOM, 0, 0) + } + + } + + fun updateSelectedCount(count: Int) { + if (count == 0) { + enableSubmit(false) + + binding.cbAll.setOnCheckedChangeListener(null) + binding.cbAll.isChecked = false + binding.cbAll.setOnCheckedChangeListener(onCheckedChangeListener) + } else { + enableSubmit(true) + binding.tvNumber.text = binding.root.context.getString(R.string.count_with_parentheses, "$count") + } + + } + + private fun enableSubmit(isEnable: Boolean) { + binding.tvSubmit.isEnabled = isEnable + if (isEnable) { + binding.tvSubmit.alpha = 1F + } else { + binding.tvSubmit.alpha = 0.4F + binding.tvNumber.setText(null) + + } + } + + companion object { + + fun create(context: Context, listener: OnReserveAllSelectListener): ReserveAllSelectPop { + val inflater = LayoutInflater.from(context) + return ReserveAllSelectPop(PopReserveAllSelectBinding.inflate(inflater), listener) + } + } + + interface OnReserveAllSelectListener { + + fun selectAll(isChecked: Boolean): Int + + fun submit() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt b/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt index 052c79561b..cb0250d2e5 100644 --- a/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt +++ b/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt @@ -5,25 +5,35 @@ import android.app.Activity import android.app.Application.ActivityLifecycleCallbacks import android.os.Bundle import android.preference.PreferenceManager +import android.text.TextUtils +import androidx.annotation.WorkerThread import androidx.fragment.app.FragmentActivity import com.gh.common.iinterface.ISuperiorChain import com.gh.common.util.CheckLoginUtils import com.gh.common.util.PackageUtils +import com.gh.download.DownloadManager import com.gh.gamecenter.SplashAdActivity import com.gh.gamecenter.SplashScreenActivity import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.entity.SimpleGameEntity +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.SPUtils.getBoolean +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.entity.DialogEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.ReserveOnlineEntity import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.hud.PendingInstallHUDHandler import com.gh.gamecenter.hud.ResumeDownloadHudHandler +import com.gh.gamecenter.login.entity.UserInfoEntity 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 /** @@ -35,6 +45,7 @@ import io.reactivex.schedulers.Schedulers object GlobalPriorityChainHelper : ISuperiorChain { private val api = RetrofitManager.getInstance().api + private val newApi = RetrofitManager.getInstance().newApi private var inferiorChain: PriorityChain? = null private val mainChain: PriorityChain by lazy { PriorityChain { inferiorChain?.start() } } @@ -223,7 +234,7 @@ object GlobalPriorityChainHelper : ISuperiorChain { /** * 请求预约弹窗相关的数据 */ - @SuppressLint("CheckResult") + private fun requestReserveDialogData(reserveDialogHandler: ReserveDialogHandler) { // debugOnly { // reserveDialogHandler.doPreProcess(arrayListOf(SimpleGameEntity( @@ -236,23 +247,145 @@ object GlobalPriorityChainHelper : ISuperiorChain { // } if (CheckLoginUtils.isLogin()) { - api.getReserveDialog(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - reserveDialogHandler.doPreProcess(data) - } - - override fun onFailure(exception: Exception) { - reserveDialogHandler.doPreProcess(null) - } - }) + val isTeenagerMode = getBoolean(Constants.SP_TEENAGER_MODE) + loadReserveDialogOnlineData(isTeenagerMode, reserveDialogHandler) } else { reserveDialogHandler.doPreProcess(null) } } + /** + * 由于用户预约的游戏个数没有限制,为避免一次加载游戏过多,这里采用先加载第一页,显示出dialog,后续数据在第二页返回 + * 注意:当page = 2 时,后台会将后续所有数据一次性返回,所以递归到第三层时,就会结束 + * @param isTeenagerMode 青少年模式关闭时才需要启动自动下载,所以当 isTeenagerMode == true 时,不需要加载后续数据,也不需要启动自动下载 + */ + @SuppressLint("CheckResult") + private fun loadReserveDialogOnlineData(isTeenagerMode: Boolean, handler: ReserveDialogHandler? = null) { + newApi.getReserveDialog() + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ReserveOnlineEntity) { + handler?.doPreProcess(data) + // 继续获取后续需要自动下载的游戏 + runOnIoThread { + loadGamesWithAutoDownload(1, 20, isTeenagerMode) + } + } + + override fun onFailure(exception: Exception) { + handler?.doPreProcess(null) + } + }) + } + + /** + * 由于这个方法本身就是在子线程调用的,这里不需要另外在做线程切换 + */ + @SuppressLint("CheckResult") + @WorkerThread + private fun loadGamesWithAutoDownload(page: Int, pageSize: Int, isTeenagerMode: Boolean) { + if (page >= 5) { + // 为防止死循环,这里设置最多请求到第5页(正常情况下,第一页就是全部数据) + return + } + val filter = "wifi_auto_download:true" + newApi.loadGamesWithAutoDownload(page, pageSize, filter) + .doOnSuccess(::trackAppointmentGameOnlineDialogShow) + .subscribe(object : BiResponse>() { + + override fun onSuccess(data: List) { + // 请注意,这是在子线程中 + if (data.isNotEmpty()) { + loadGamesWithAutoDownload(page + 1, pageSize, isTeenagerMode) + startAutoDownloadIfNeed(data, isTeenagerMode) + } + + } + + }) + } + + @SuppressLint("CheckResult") + private fun startAutoDownloadIfNeed(games: List, isTeenagerMode: Boolean) { + val autoDownloadGameIds = mutableSetOf() + games.forEach { + autoDownloadGameIds.add(it.id) + if (it.wifiAutoDownload && !it.isLandPageAddressDialog() && !isTeenagerMode) { + // 开启了 wifi 自动下载,并且不能为第三方落地页跳转 + val apk = when (it.getApk().size) { + 0 -> null + 1 -> it.getApk().first() + else -> { + // 自动下载,多版本默认:九游版 + it.getApk().find { apk -> "9u" == apk.getPlatform() && apk.isActive } + } + } + if (apk != null) { + val updateEntities = PackageUtils.getUpdateData(it, false) + val isCanUpdate = if (updateEntities.size == 1) { + true + } else { + // 多版本只需要判断九游版是否需要更新 + updateEntities.any { update -> "9u" == update.platform } + } + val isCanPluggable = PackageUtils.isCanPluggable(apk) + if ((!PackageUtils.isInstalled(HaloApp.getInstance(), apk.packageName) + || isCanUpdate + || isCanPluggable) + && DownloadManager.getInstance().getDownloadEntityByUrl(apk.url) == null + ) { + // 未下载/可更新/可插件化 且 之前没有下载记录 + val msg = FileUtils.isCanDownload(HaloApp.getInstance(), apk.size ?: "") + if (msg.isNullOrBlank()) { + val isWifiOpen = NetworkUtils.isWifiConnected(HaloApp.getInstance()) + val isSubscribe = !isWifiOpen + DownloadManager.createDownload( + HaloApp.getInstance(), + apk, + it, + false, + false, + "", + "", + isSubscribe, + ExposureEvent.createEvent(it, listOf(ExposureSource("预约上线弹窗-自动下载", ""))) + ) + } else { + // 存储空间不足,直接跳出循环,放弃后续的所有游戏 + ToastUtils.showToast(msg) + return@forEach + } + } + } + } + } + if (autoDownloadGameIds.isNotEmpty()) { + val body = hashMapOf( + "game_ids" to autoDownloadGameIds, + "type" to "wifi_auto_download" + ) + newApi.logAutoDownload(body) + .subscribe({}, {}) + } + } + + private fun trackAppointmentGameOnlineDialogShow(games: List) { + if (games.isNotEmpty()) { + val gameIds = arrayListOf() + val gameNames = arrayListOf() + val gameTypes = arrayListOf() + games.forEach { game -> + gameIds.add(game.id) + gameNames.add(game.name ?: "") + gameTypes.add(game.categoryChinese) + } + val gameId = gameIds.joinToString() + val gameName = gameNames.joinToString() + val gameType = gameTypes.joinToString() + SensorsBridge.trackAppointmentGameOnlineDialogShow(gameId, gameName, gameType) + } + } + override fun registerInferiorChain(chain: PriorityChain) { inferiorChain = chain if (mainChain.isHandlerQueueEmpty()) { diff --git a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt index c24bc7ff8d..8ee3a0775b 100644 --- a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt @@ -2,28 +2,28 @@ package com.gh.common.prioritychain import androidx.fragment.app.FragmentActivity import com.gh.common.dialog.ReserveDialog -import com.gh.gamecenter.common.entity.SimpleGameEntity import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.feature.entity.ReserveOnlineEntity class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private var mReserveData: List? = null + private var mReserveData: ReserveOnlineEntity? = null /** * 提前预处理显示弹窗的内容 */ - fun doPreProcess(reserveData: List?) { + fun doPreProcess(reserveData: ReserveOnlineEntity?) { mReserveData = reserveData if (getStatus() == STATUS_PENDING) { - if (reserveData.isNullOrEmpty()) { + if (reserveData?.games.isNullOrEmpty()) { processNext() } else { updateStatus(STATUS_VALID) process() } } else { - if (reserveData.isNullOrEmpty()) { + if (reserveData?.games.isNullOrEmpty()) { updateStatus(STATUS_INVALID) } else { updateStatus(STATUS_VALID) @@ -37,7 +37,7 @@ class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { when (getStatus()) { STATUS_VALID -> { - val reserveDialog = ReserveDialog.getInstance(mReserveData!!) + val reserveDialog = ReserveDialog.getInstance(mReserveData ?:ReserveOnlineEntity()) reserveDialog.setOnDismissListener { processNext() } diff --git a/app/src/main/java/com/gh/common/provider/WechatHelperProviderImpl.kt b/app/src/main/java/com/gh/common/provider/WechatHelperProviderImpl.kt index d594b52248..930cd319a6 100644 --- a/app/src/main/java/com/gh/common/provider/WechatHelperProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/WechatHelperProviderImpl.kt @@ -4,16 +4,19 @@ import android.content.Context import com.alibaba.android.arouter.facade.annotation.Route import com.gh.common.util.WechatBindHelper import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.entity.WechatConfigEntity import com.gh.gamecenter.core.provider.IWechatBindHelperProvider @Route(path = RouteConsts.provider.wechatHelper, name = "WechatHelper暴露服务") -class WechatHelperProviderImpl : IWechatBindHelperProvider { - override fun getWechatConfig(callback: (() -> Unit)?) { +class WechatHelperProviderImpl : IWechatBindHelperProvider { + + override fun getWechatConfig(callback: ((WechatConfigEntity) -> Unit)?) { WechatBindHelper.getWechatConfig { - callback?.invoke() + callback?.invoke(it) } } + override fun init(context: Context?) { // Do nothing } diff --git a/app/src/main/java/com/gh/common/util/DialogUtils.java b/app/src/main/java/com/gh/common/util/DialogUtils.java index db8cf978f9..6b13bed717 100644 --- a/app/src/main/java/com/gh/common/util/DialogUtils.java +++ b/app/src/main/java/com/gh/common/util/DialogUtils.java @@ -2,6 +2,7 @@ package com.gh.common.util; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.Intent; @@ -66,6 +67,7 @@ import com.gh.gamecenter.databinding.DialogReportReasonBinding; import com.gh.gamecenter.databinding.DialogWechatReserveSuccessBinding; import com.gh.gamecenter.databinding.ImprintContentItemBinding; import com.gh.gamecenter.entity.BadgeEntity; +import com.gh.gamecenter.entity.ReserveReminderEntity; import com.gh.gamecenter.entity.TrackableEntity; import com.gh.gamecenter.feature.entity.ApkEntity; import com.gh.gamecenter.feature.entity.Badge; @@ -73,6 +75,7 @@ import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.SettingsEntity; import com.gh.gamecenter.help.HelpAndFeedbackBridge; import com.gh.gamecenter.setting.SettingBridge; +import com.halo.assistant.fragment.reserve.ReserveReminderContainerFragment; import com.lightgame.download.DownloadEntity; import com.lightgame.utils.AppManager; import com.lightgame.utils.Util_System_Keyboard; @@ -148,6 +151,7 @@ public class DialogUtils { } return false; } + public static void showNoConnectionDownloadDialog(Context context, ConfirmListener listener, CancelListener cancelListener) { DialogHelper.showDialog(context, "下载提示", "网络异常,请检查手机网络状态", "知道了", "WiFi自动下载", listener::onConfirm, cancelListener::onCancel, false, "", ""); } @@ -1224,7 +1228,7 @@ public class DialogUtils { final Dialog dialog = new Dialog(context, com.gh.gamecenter.common.R.style.DialogWindowTransparent); DialogWechatReserveSuccessBinding binding = DialogWechatReserveSuccessBinding.inflate(LayoutInflater.from(context)); binding.titleIv.setImageResource(isReserve ? R.drawable.bg_reserve_success : R.drawable.bg_vote_success); - binding.contentTv.setText(isReserve ? "游戏上线时,您将在消息中心收到通知。为了避免错过通知,建议您开启微信公众号提醒": "版本上线时,您将在消息中心收到通知。为了避免错过通知,亦建议您开启微信公众号提醒"); + binding.contentTv.setText(isReserve ? "游戏上线时,您将在消息中心收到通知。为了避免错过通知,建议您开启微信公众号提醒" : "版本上线时,您将在消息中心收到通知。为了避免错过通知,亦建议您开启微信公众号提醒"); binding.closeBtn.setOnClickListener(v -> { cancelListener.onCancel(); dialog.dismiss(); @@ -1269,6 +1273,10 @@ public class DialogUtils { } } + public static void showReserveReminderDialog(Context context, GameEntity gameEntity, ReserveReminderEntity reserveReminder) { + ReserveReminderContainerFragment.show(context, gameEntity, reserveReminder); + } + public static void showRelievePhoneDialog(Context context) { context = checkDialogContext(context); diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt index 33b0bd9c3c..acebdf74d2 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt @@ -27,6 +27,8 @@ import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity +import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity import com.gh.gamecenter.common.callback.CancelListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts @@ -128,6 +130,7 @@ object DownloadItemUtils { holder.gameDownloadBtn.text = "已预约" holder.gameDownloadBtn.visibility = View.VISIBLE holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.RESERVED + holder.multiVersionDownloadTv.visibility = View.GONE updateItemViewStatus(holder, null, null) } } @@ -297,11 +300,13 @@ object DownloadItemUtils { // 来自于下载管理的实体快照 val entityFromDownloadManager = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) // 是否正在下载中 - val isDownloading = entityFromDownloadManager != null && entityFromDownloadManager.status != DownloadStatus.done + val isDownloading = + entityFromDownloadManager != null && entityFromDownloadManager.status != DownloadStatus.done // 是否已安装至本地 val isInstalledLocally = PackagesManager.isInstalled(gameEntity.getUniquePackageName()) // 来自于畅玩安装列表的实体快照,若存在,代表游戏已下载并成功安装 - val entityFromInstalledVGame = VHelper.getVDownloadEntitySnapshot(gameEntity.id, gameEntity.getUniquePackageName()) + val entityFromInstalledVGame = + VHelper.getVDownloadEntitySnapshot(gameEntity.id, gameEntity.getUniquePackageName()) // 是否已安装至畅玩 val isInstalledToVSpace = entityFromInstalledVGame != null // 是否处于待安装状态 (仅本地安装有这个状态) @@ -318,19 +323,20 @@ object DownloadItemUtils { // 3. 存在待安装任务时,待安装任务优先 // 4. 存在一个已安装,已安装的显示优先 // 5. 都已安装,按后台配置显示的状态优先 - val isVGamePreferred = if (!isInstalledLocally && !isInstalledToVSpace && entityFromDownloadManager == null) { - gameEntity.isVGamePreferred() - } else if (isDownloading || isPendingToInstall) { - entityFromDownloadManager?.isVGameDownloadInDualDownloadMode() == true - } else if (isInstalledLocally && isInstalledToVSpace) { - gameEntity.isVGamePreferred() - } else if (isInstalledLocally) { - false - } else if (isInstalledToVSpace) { - true - } else { - gameEntity.isVGamePreferred() - } + val isVGamePreferred = + if (!isInstalledLocally && !isInstalledToVSpace && entityFromDownloadManager == null) { + gameEntity.isVGamePreferred() + } else if (isDownloading || isPendingToInstall) { + entityFromDownloadManager?.isVGameDownloadInDualDownloadMode() == true + } else if (isInstalledLocally && isInstalledToVSpace) { + gameEntity.isVGamePreferred() + } else if (isInstalledLocally) { + false + } else if (isInstalledToVSpace) { + true + } else { + gameEntity.isVGamePreferred() + } val downloadEntity: DownloadEntity? = if (isDownloading) { entityFromDownloadManager @@ -386,7 +392,11 @@ object DownloadItemUtils { if (PackagesManager.isInstalled(downloadEntity.packageName) && !downloadEntity.isUpdate) { // 双下载按钮快速安装时存在已下载的安装包过时,需要重新下载的情况 - if (PackagesManager.isCanUpdate(downloadEntity.gameId, downloadEntity.packageName)) { + if (PackagesManager.isCanUpdate( + downloadEntity.gameId, + downloadEntity.packageName + ) + ) { buttonStyle = DownloadButton.ButtonStyle.NORMAL setText(com.gh.gamecenter.feature.R.string.update) } else { @@ -626,7 +636,17 @@ object DownloadItemUtils { location: String, sourceEntrance: String = "其他" ) { - setOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, sourceEntrance, location, null) + setOnClickListener( + context, + downloadBtn, + gameEntity, + position, + adapter, + entrance, + sourceEntrance, + location, + null + ) } @JvmStatic @@ -641,7 +661,18 @@ object DownloadItemUtils { location: String, traceEvent: ExposureEvent?, ) { - setOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, sourceEntrance, location, traceEvent, null) + setOnClickListener( + context, + downloadBtn, + gameEntity, + position, + adapter, + entrance, + sourceEntrance, + location, + traceEvent, + null + ) } /** @@ -779,14 +810,18 @@ object DownloadItemUtils { if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.id)) { SensorsBridge.trackEvent( "AppointmentGame", - "game_name", - gameEntity.name ?: "", - "game_id", - gameEntity.id, - "game_type", - gameEntity.categoryChinese, - "source_entrance", - sourceEntrance + "game_name", gameEntity.name ?: "", + "game_id", gameEntity.id, + "game_type", gameEntity.categoryChinese, + "source_entrance", sourceEntrance, + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "source", gameEntity.exposureEvent?.source?.toString() ?: "", + *gameEntity.customPageTrackData?.toKV() ?: arrayOf() ) allStateClickCallback?.onCallback() CheckLoginUtils.checkLogin(context, entrance) { @@ -812,7 +847,7 @@ object DownloadItemUtils { } } } else { - ReservationHelper.showCancelReservationDialog(context, { + ReservationHelper.showCancelReservationDialog(context, gameEntity,{ NewFlatLogUtils.logMyGameCancelReserveDialogClick( "确定取消", gameEntity.id, @@ -1057,7 +1092,8 @@ object DownloadItemUtils { NewSimulatorGameManager.showUpdateNewsSimulator(context, gameEntity, null) return } - val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(gameEntity.getApk().firstOrNull()?.url) + val downloadEntity = + SimulatorGameManager.findDownloadEntityByUrl(gameEntity.getApk().firstOrNull()?.url) if (downloadEntity != null) { val file = File(downloadEntity.path) if (!file.exists()) { diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index 07df932622..4ca94e2e11 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -54,6 +54,12 @@ object DownloadObserver { private val mRetryableHashMap = hashMapOf() + /** + * 当下载任务是 预约上线提醒 触发的,则所有弹窗均不显示 + */ + private val DownloadEntity.isDownloadByReserveOnlineReminder: Boolean + get() = !meta?.get(Constants.IS_RESERVE_ONLINE_REMINDER).isNullOrBlank() + // 如果在WIFI状态下,下载自动暂停,则再重试一遍 @JvmStatic fun initObserver() { @@ -74,6 +80,9 @@ object DownloadObserver { if (DownloadStatus.hijack == status) { // 链接被劫持 + if (downloadEntity.isDownloadByReserveOnlineReminder) { + return + } processHijack(downloadEntity) return } else if (DownloadStatus.notfound == status) { @@ -90,7 +99,9 @@ object DownloadObserver { gameName = downloadEntity.name, gameType = downloadEntity.categoryChinese ) - + if (downloadEntity.isDownloadByReserveOnlineReminder) { + return + } DialogHelper.showDialog( currentActivity, "下载失败", @@ -159,7 +170,9 @@ object DownloadObserver { DownloadDataHelper.uploadRedirectEvent(downloadEntity) } else if (DownloadStatus.unqualified == status) { // 未成年 - RealNameHelper.showRealNameUnqualifiedDialog(downloadEntity) + if (!downloadEntity.isDownloadByReserveOnlineReminder) { + RealNameHelper.showRealNameUnqualifiedDialog(downloadEntity) + } // 删除任务 downloadEntity.status = DownloadStatus.cancel @@ -168,7 +181,7 @@ object DownloadObserver { // 未接入防沉迷系统 val currentActivity = AppManager.getInstance().currentActivity() - if (currentActivity != null) { + if (currentActivity != null && !downloadEntity.isDownloadByReserveOnlineReminder) { DialogHelper.showDialog( context = currentActivity, title = "温馨提示", @@ -187,7 +200,7 @@ object DownloadObserver { // 未接入防沉迷系统 val currentActivity = AppManager.getInstance().currentActivity() - if (currentActivity != null) { + if (currentActivity != null && !downloadEntity.isDownloadByReserveOnlineReminder) { DialogHelper.showDialog( context = currentActivity, title = "实名提示", @@ -213,7 +226,9 @@ object DownloadObserver { downloadManager.cancel(downloadEntity.url) } else if (DownloadStatus.uncertificated == status) { // 未实名 - RealNameHelper.showRealNameUncertificatedDialog(downloadEntity) + if (!downloadEntity.isDownloadByReserveOnlineReminder) { + RealNameHelper.showRealNameUncertificatedDialog(downloadEntity) + } // 删除任务 downloadEntity.status = DownloadStatus.cancel @@ -228,11 +243,15 @@ object DownloadObserver { if (DownloadStatus.done == status) { if (mDoneDebouncePair?.first != downloadEntity.url) { mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis()) + // 清理临时变量 + downloadEntity.addMetaExtra(Constants.IS_RESERVE_ONLINE_REMINDER, "") performDownloadCompleteAction(downloadEntity, downloadManager) } else { if (mDoneDebouncePair?.second == 0L || System.currentTimeMillis() - (mDoneDebouncePair?.second ?: 0) > 500 ) { + // 清理临时变量 + downloadEntity.addMetaExtra(Constants.IS_RESERVE_ONLINE_REMINDER, "") performDownloadCompleteAction(downloadEntity, downloadManager) } } @@ -513,7 +532,7 @@ object DownloadObserver { "space_schema_type", if (downloadEntity.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) "32位" else "64位" ) - } else if(downloadEntity.gameId == Constants.HALO_FUN_NEW_32_GAME_ID) { + } else if (downloadEntity.gameId == Constants.HALO_FUN_NEW_32_GAME_ID) { SensorsBridge.trackEvent( "HaloFunDownloadDone", "space_schema_type", diff --git a/app/src/main/java/com/gh/common/util/ErrorHelper.kt b/app/src/main/java/com/gh/common/util/ErrorHelper.kt index 78cdd7fd82..6ebeee07b8 100644 --- a/app/src/main/java/com/gh/common/util/ErrorHelper.kt +++ b/app/src/main/java/com/gh/common/util/ErrorHelper.kt @@ -7,13 +7,13 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import com.gh.common.constant.Config +import com.gh.common.dialog.WechatBindingFailedDialogFragment import com.gh.gamecenter.R import com.gh.gamecenter.ShellActivity import com.gh.gamecenter.VerifyPhoneActivity import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.avoidcallback.AvoidOnResultManager import com.gh.gamecenter.common.avoidcallback.Callback -import com.gh.gamecenter.common.callback.CancelListener import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts @@ -191,7 +191,7 @@ object ErrorHelper { 403018 -> Utils.toast(context, com.gh.gamecenter.common.R.string.comment_failed_unable) 403070 -> Utils.toast(context, "请勿重复提交~") 403073 -> Utils.toast(context, "标题违规,请重新编辑") - 403074 -> Utils.toast(context, "该微信号(${errorEntity.data?.nickname})已绑定") + 403074 -> handleErrorWithRepeatWechatBinding(context, errorEntity) 403078 -> Utils.toast(context, "已点赞") 403072 -> Utils.toast(context, R.string.comment_failed_userblocked) 403082 -> Utils.toast(context, "作者已关闭评论") @@ -234,6 +234,10 @@ object ErrorHelper { } } + private fun handleErrorWithRepeatWechatBinding(context: Context, errorEntity: ErrorEntity) { + WechatBindingFailedDialogFragment.show(context, errorEntity.data?.userConflict) + } + private fun handleAskFrequentlyError(context: Context, showHighPriorityHint: Boolean) { if (showHighPriorityHint) { DialogHelper.showDialog( @@ -261,7 +265,7 @@ object ErrorHelper { SensorsBridge.trackVerificationDialogShow( gameId = gameEntity?.id ?: "", gameName = gameEntity?.name ?: "", - gameType = gameEntity?.categoryChinese ?:"", + gameType = gameEntity?.categoryChinese ?: "", articleType = articleType, verificationType = verificationType ) diff --git a/app/src/main/java/com/gh/common/util/NewLogUtils.kt b/app/src/main/java/com/gh/common/util/NewLogUtils.kt index a82aea46f1..b19cacda2c 100644 --- a/app/src/main/java/com/gh/common/util/NewLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewLogUtils.kt @@ -1815,60 +1815,6 @@ object NewLogUtils { log(json, LOG_STORE_EVENT) } - //成功预约游戏 - @JvmStatic - fun logReserveGameSuccess(wechatConfigEntity: WechatConfigEntity) { - val json = json { - KEY_EVENT to "appointment_wechat_appointment_game" - "wechat_is_bind" to wechatConfigEntity.bind - "wechat_is_follow" to wechatConfigEntity.follow - "wechat_is_remind" to wechatConfigEntity.notice - KEY_TIMESTAMP to System.currentTimeMillis() / 1000 - parseAndPutMeta().invoke(this) - } - log(json, "appointment", false) - } - - //引导设置微信提醒弹窗事件 - @JvmStatic - fun logReserveWechatRemindPopShow(wechatConfigEntity: WechatConfigEntity) { - val json = json { - KEY_EVENT to "appointment_wechat_remind_pop_show" - "wechat_is_bind" to wechatConfigEntity.bind - "wechat_is_follow" to wechatConfigEntity.follow - "wechat_is_remind" to wechatConfigEntity.notice - KEY_TIMESTAMP to System.currentTimeMillis() / 1000 - parseAndPutMeta().invoke(this) - } - log(json, "appointment", false) - } - - //引导设置微信提醒弹窗点击事件 - @JvmStatic - fun logReserveWechatRemindPopClick(wechatConfigEntity: WechatConfigEntity, operationType: String) { - val json = json { - KEY_EVENT to "appointment_wechat_remind_pop_click" - "wechat_is_bind" to wechatConfigEntity.bind - "wechat_is_follow" to wechatConfigEntity.follow - "wechat_is_remind" to wechatConfigEntity.notice - "operation_type" to operationType - KEY_TIMESTAMP to System.currentTimeMillis() / 1000 - parseAndPutMeta().invoke(this) - } - log(json, "appointment", false) - } - - //预约成功弹窗事件 - @JvmStatic - fun logReserveWechatSuccessPopShow() { - val json = json { - KEY_EVENT to "appointment_wechat_success_pop_show" - KEY_TIMESTAMP to System.currentTimeMillis() / 1000 - parseAndPutMeta().invoke(this) - } - log(json, "appointment", false) - } - //选择图片上传方式 @JvmStatic fun logShowGameCollectionCoverTypeDialog() { diff --git a/app/src/main/java/com/gh/common/util/ReservationHelper.kt b/app/src/main/java/com/gh/common/util/ReservationHelper.kt index 6afeb907ff..145dd88e32 100644 --- a/app/src/main/java/com/gh/common/util/ReservationHelper.kt +++ b/app/src/main/java/com/gh/common/util/ReservationHelper.kt @@ -2,21 +2,30 @@ package com.gh.common.util import android.annotation.SuppressLint import android.content.Context +import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.constant.Constants import com.gh.common.repository.ReservationRepository import com.gh.gamecenter.WebActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity +import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity import com.gh.gamecenter.common.callback.CancelListener import com.gh.gamecenter.common.callback.ConfirmListener +import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.entity.WechatConfigEntity import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.provider.IPushProvider +import com.gh.gamecenter.entity.ReserveReminderEntity +import com.gh.gamecenter.jg.push.service.HaloJPushMessageReceiver import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers +import okhttp3.RequestBody import okhttp3.ResponseBody object ReservationHelper { @@ -37,18 +46,22 @@ object ReservationHelper { deleteReservation: Boolean, refreshCallback: EmptyCallback ) { - val retrofit = RetrofitManager.getInstance() - val requestMap = hashMapOf() - requestMap["game_id"] = game.id + val retrofit = RetrofitManager.getInstance() val single = if (deleteReservation) { - retrofit.api - .deleteGameReservation(requestMap.createRequestBody()) + retrofit.newApi + .deleteGameReservation( + game.id, + getReserveRequestBody(game, context = HaloApp.getInstance().application) + ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } else { - retrofit.api - .cancelGameReservation(requestMap.createRequestBody()) + retrofit.newApi + .cancelGameReservation( + game.id, + getReserveRequestBody(game, context = HaloApp.getInstance().application) + ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } @@ -69,71 +82,65 @@ object ReservationHelper { @JvmStatic @SuppressLint("CheckResult") fun reserve(context: Context, game: GameEntity?, sourceEntrance: String = "其他", callback: EmptyCallback) { - val requestMap = hashMapOf() - requestMap["game_id"] = game?.id ?: "" - RetrofitManager.getInstance().api - .createNewGameReservation(requestMap.createRequestBody()) - .compose(singleToMain()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { + RetrofitManager.getInstance().newApi + .createNewGameReservation(game?.id ?: "", getReserveRequestBody(game, context)) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + + override fun onSuccess(data: ReserveReminderEntity) { SensorsBridge.trackEvent( "AppointmentGameResult", - "game_name", - game?.name ?: "", - "game_id", - game?.id ?: "", - "game_type", - game?.categoryChinese ?: "", - "result", - "成功", - "source_entrance", - sourceEntrance + "game_name", game?.name ?: "", + "game_id", game?.id ?: "", + "game_type", game?.categoryChinese ?: "", + "result", "成功", + "source_entrance", sourceEntrance, + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "source", game?.exposureEvent?.source?.toString() ?: "", + *game?.customPageTrackData?.toKV() ?: arrayOf() ) ReservationRepository.addReservationToMemoryAndRefresh(game?.id ?: "") callback.onCallback() - val wechatConfig = SPUtils.getString(Constants.SP_WECHAT_CONFIG).toObject() - wechatConfig?.run { - NewLogUtils.logReserveGameSuccess(wechatConfig) - if (bind && follow && notice) { - NewLogUtils.logReserveWechatSuccessPopShow() - DialogUtils.showReserveOrVoteSuccessDialog(context, true) - } else { - NewLogUtils.logReserveWechatRemindPopShow(wechatConfig) - SensorsBridge.trackEvent("AppointmenWechatRemindDialogShow") - DialogUtils.showReserveOrVoteSuccess2WechatBindDialog(context, true, { - NewLogUtils.logReserveWechatRemindPopClick(wechatConfig, "开启微信提醒") - SensorsBridge.trackEvent("AppointmenWechatRemindDialogClick") - context.startActivity(WebActivity.getBindWechatIntent(context)) - SensorsBridge.trackEvent( - "AppointmenWechatRemindConfigPageShow", - "source_entrance", - "设置微信提醒弹窗" - ) - }, object : CancelListener { - override fun onCancel() { - NewLogUtils.logReserveWechatRemindPopClick(wechatConfig, "关闭弹窗") - SensorsBridge.trackEvent("AppointmenWechatRemindDialogClick") - } - }) - } + + checkWechatConfigEntityUpdated(data.wechatConfig) + + game?.let { + SensorsBridge.trackAppointmentSuccessDialogShow( + it.id, + it.name ?: "", + it.categoryChinese, + data.wechatConfig.isReminderEnable, + if (data.hasSmsConfig) data.smsConfig.notice else null, + if (data.isEnableAutoDownload) data.wifiAutoDownload else null + ) } + + DialogUtils.showReserveReminderDialog(context, game, data) } override fun onFailure(exception: Exception) { SensorsBridge.trackEvent( "AppointmentGameResult", - "game_name", - game?.name ?: "", - "game_id", - game?.id ?: "", - "game_type", - game?.categoryChinese ?: "", - "result", - "失败", - "source_entrance", - sourceEntrance + "game_name", game?.name ?: "", + "game_id", game?.id ?: "", + "game_type", game?.categoryChinese ?: "", + "result", "失败", + "source_entrance", sourceEntrance, + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "source", game?.exposureEvent?.source?.toString() ?: "", + *game?.customPageTrackData?.toKV() ?: arrayOf() ) ToastUtils.showToast(exception.message ?: "") } @@ -156,24 +163,85 @@ object ReservationHelper { } @JvmStatic - fun showCancelReservationDialog(context: Context, emptyCallback: EmptyCallback) { - showCancelReservationDialog(context, emptyCallback, null) + fun showCancelReservationDialog(context: Context, game: GameEntity?, emptyCallback: EmptyCallback) { + showCancelReservationDialog(context, game, emptyCallback, null) } @JvmStatic - fun showCancelReservationDialog(context: Context, emptyCallback: EmptyCallback, cancelListener: CancelListener?) { + fun showCancelReservationDialog( + context: Context, + game: GameEntity?, + emptyCallback: EmptyCallback, + cancelListener: CancelListener?, + dialogCancelCallback: (() -> Unit)? = null + ) { + if (game != null) { + SensorsBridge.trackCancelAppointmentDialogShow(game.id, game.name ?: "", game.categoryChinese) + } DialogHelper.showDialog(context, "取消预约", "取消之后你将无法收到游戏上线的通知,确定取消预约吗?", "确定取消", "暂不取消", confirmClickCallback = { + if (game != null) { + SensorsBridge.trackCancelAppointmentDialogClick( + game.id, + game.name ?: "", + game.categoryChinese, + "确定取消" + ) + } emptyCallback.onCallback() }, cancelClickCallback = { + if (game != null) { + SensorsBridge.trackCancelAppointmentDialogClick( + game.id, + game.name ?: "", + game.categoryChinese, + "暂不取消" + ) + } cancelListener?.onCancel() }, uiModificationCallback = { it.confirmTv.setTextColor(com.gh.gamecenter.common.R.color.secondary_red.toColor(context)) - }, extraConfig = DialogHelper.Config(centerContent = true, centerTitle = true) + }, extraConfig = DialogHelper.Config(centerContent = true, centerTitle = true), + dialogCancelCallback = { + if (game != null) { + SensorsBridge.trackCancelAppointmentDialogClick( + game.id, + game.name ?: "", + game.categoryChinese, + "关闭弹窗" + ) + } + dialogCancelCallback?.invoke() + } ) } + private fun checkWechatConfigEntityUpdated(newWechatConfig: WechatConfigEntity) { + val wechatConfig = SPUtils.getString(Constants.SP_WECHAT_CONFIG).toObject() + if (newWechatConfig.bind != wechatConfig?.bind + || newWechatConfig.follow != wechatConfig.follow + || newWechatConfig.notice != wechatConfig.notice + || newWechatConfig.nickName != wechatConfig.nickName + ) { + SPUtils.setString(Constants.SP_WECHAT_CONFIG, newWechatConfig.toJson()) + } + } + @JvmStatic + fun getReserveRequestBody(game: GameEntity?, context: Context): RequestBody { + val jPushId = + (ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider) + ?.getRegistrationId(context) ?: "" + var mirrorPosition = game?.getMirrorPosition() ?: 0 + if (mirrorPosition == -1) { + mirrorPosition = 0 + } + val body = hashMapOf( + "jpush_id" to jPushId, + "mirror_type" to mirrorPosition + ).toRequestBody() + return body + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/WechatBindHelper.kt b/app/src/main/java/com/gh/common/util/WechatBindHelper.kt index c103973650..e8ccc192e9 100644 --- a/app/src/main/java/com/gh/common/util/WechatBindHelper.kt +++ b/app/src/main/java/com/gh/common/util/WechatBindHelper.kt @@ -22,7 +22,7 @@ object WechatBindHelper { fun getWechatConfig(callback: ((WechatConfigEntity) -> Unit)? = null) { if (!UserManager.getInstance().isLoggedIn) return RetrofitManager.getInstance() - .api + .newApi .wechatConfig .compose(singleToMain()) .subscribe(object : BiResponse() { @@ -36,7 +36,7 @@ object WechatBindHelper { @SuppressLint("CheckResult") fun bindWechat(wechatLoginInfoMap: Map, callback: BiCallback) { RetrofitManager.getInstance() - .api + .newApi .postBindWechat(wechatLoginInfoMap.createRequestBody()) .compose(singleToMain()) .subscribe(object : BiResponse() { diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index 81eb5daf23..b2123ad52c 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -1,5 +1,7 @@ package com.gh.download; +import static com.gh.gamecenter.common.constant.Constants.IS_RESERVE_ONLINE_REMINDER; + import android.content.Context; import android.content.Intent; import android.os.Build; @@ -413,6 +415,12 @@ public class DownloadManager implements DownloadStatusListener { downloadEntity.setCustomPageTrackData(trackJson); } + // 是否时预约上线提醒时,开启的自动下载(如果是这种情况开启的自动下载,则下载过程中的验证弹窗全部不显示) + boolean wifiAutoDownload = gameEntity.getWifiAutoDownload(); + if (wifiAutoDownload) { + // 注意,这只是临时变量,用户在下载过程中,判断当前任务是否是预约上线触发的下载,下载完成后,请及时清理此字段 + ExtensionsKt.addMetaExtra(downloadEntity, IS_RESERVE_ONLINE_REMINDER, IS_RESERVE_ONLINE_REMINDER); + } if (isSubscribe) { DownloadManager.getInstance().subscribe(downloadEntity); } else { diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt index b82767db8c..4b359494f2 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt @@ -27,6 +27,8 @@ import com.gh.download.dialog.DownloadDialog import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity +import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity +import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.eventbus.EBReuse @@ -212,7 +214,8 @@ class DetailViewHolder( && downloadButton.buttonStyle !== ButtonStyle.INSTALL_PLUGIN && downloadButton.buttonStyle !== ButtonStyle.LAUNCH_OR_OPEN && downloadButton.buttonStyle !== ButtonStyle.NONE - && downloadButton.buttonStyle !== ButtonStyle.NONE_WITH_HINT) { + && downloadButton.buttonStyle !== ButtonStyle.NONE_WITH_HINT + ) { // 畅玩游戏的非真实点击下载按钮下载不需要滚动到特定地方 if (!mAsVGame || VHelper.shouldLaunchGameAfterInstallation()) { EventBus.getDefault().post(EBScroll(Constants.EB_GAME_DETAIL, mGameEntity.id)) @@ -306,7 +309,8 @@ class DetailViewHolder( NewSimulatorGameManager.showUpdateNewsSimulator(mViewHolder.context, mGameEntity, null) return } - val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(mGameEntity.getApk().firstOrNull()?.url) + val downloadEntity = + SimulatorGameManager.findDownloadEntityByUrl(mGameEntity.getApk().firstOrNull()?.url) if (downloadEntity != null) { val file = File(downloadEntity.path) if (!file.exists()) { @@ -431,8 +435,15 @@ class DetailViewHolder( mGameEntity.id, "game_type", mGameEntity.categoryChinese, - "source_entrance", - "其他" + "source_entrance", "其他", + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "source", mGameEntity.exposureEvent?.source?.toString() ?: "", + *mGameEntity.customPageTrackData?.toKV() ?: arrayOf() ) CheckLoginUtils.checkLogin(mViewHolder.context, mEntrance) { ReservationHelper.reserve(mViewHolder.context, mGameEntity) { @@ -451,7 +462,7 @@ class DetailViewHolder( } } } else { - ReservationHelper.showCancelReservationDialog(mViewHolder.context) { + ReservationHelper.showCancelReservationDialog(mViewHolder.context, mGameEntity) { ReservationHelper.cancelReservation(mGameEntity) { DetailDownloadUtils.updateViewHolder(mViewHolder) } @@ -485,6 +496,7 @@ class DetailViewHolder( ToastUtils.toast("正在加急更新版本,敬请后续留意") showLandPageAddressDialogIfNeeded() } + ButtonStyle.TEENAGER_MODE -> { SensorsBridge.trackAdolescentModeDialogShow( mGameEntity.id, @@ -508,11 +520,11 @@ class DetailViewHolder( }, negativeClickCallback = { SensorsBridge.trackAdolescentModeDialogClick( - "关闭弹窗", - mGameEntity.id, - (if (mGameEntity.name != null) mGameEntity.name else "")!!, - mGameEntity.categoryChinese - ) + "关闭弹窗", + mGameEntity.id, + (if (mGameEntity.name != null) mGameEntity.name else "")!!, + mGameEntity.categoryChinese + ) } ) { SensorsBridge.trackAdolescentModeDialogClick( @@ -546,7 +558,8 @@ class DetailViewHolder( DownloadManager.getInstance().resume(mDownloadEntity, false) } else { DownloadManager.getInstance().pause(mDownloadEntity!!.url) - downloadButton.text = "${DetailDownloadUtils.getValidProgress(mDownloadEntity)}% $mPausedText" + downloadButton.text = + "${DetailDownloadUtils.getValidProgress(mDownloadEntity)}% $mPausedText" } } } @@ -620,7 +633,8 @@ class DetailViewHolder( if (mShowDualDownloadButton && downloadEntity != null && downloadEntity.status == DownloadStatus.done - && !FileUtils.isEmptyFile(downloadEntity.path)) { + && !FileUtils.isEmptyFile(downloadEntity.path) + ) { if (downloadEntity.getMetaExtra(Constants.APK_MD5) != mGameEntity.getApk().firstOrNull()?.md5) { // 已下载的 md5 与接口返回的不一样,不使用快速安装功能,重新下载安装 diff --git a/app/src/main/java/com/gh/gamecenter/entity/ReserveModifyEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ReserveModifyEntity.kt new file mode 100644 index 0000000000..fa116d286b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ReserveModifyEntity.kt @@ -0,0 +1,12 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +class ReserveModifyEntity( + @SerializedName("sms_config") + private val _smsConfig: ReserveReminderEntity.SmsConfig? = null +) { + + val smsConfig: ReserveReminderEntity.SmsConfig + get() = _smsConfig ?: ReserveReminderEntity.SmsConfig() +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ReserveReminderEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ReserveReminderEntity.kt new file mode 100644 index 0000000000..1d2883c72d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ReserveReminderEntity.kt @@ -0,0 +1,84 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.gh.gamecenter.common.entity.WechatConfigEntity +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +class ReserveReminderEntity( + @SerializedName("sms_config") + private var _smsConfig: SmsConfig? = null, + @SerializedName("wechat_config") + private var _wechatConfig: WechatConfigEntity? = null, + @SerializedName("mirror_type") + private val _mirrorType: String? = null, + @SerializedName("wifi_auto_download") + private val _wifiAutoDownload: Boolean? = null, + @SerializedName("jpush_id") + private val _jpushId: String? = null +) : Parcelable { + + val hasSmsConfig: Boolean + get() = _smsConfig != null + + var smsConfig: SmsConfig + get() = _smsConfig ?: SmsConfig() + set(value) { + _smsConfig = value + } + + var wechatConfig: WechatConfigEntity + get() = _wechatConfig ?: WechatConfigEntity() + set(value) { + _wechatConfig = value + } + + val onlyShowWechatReminder: Boolean + get() = _smsConfig == null + + val mirrorType: String + get() = _mirrorType ?: "" + + val wifiAutoDownload: Boolean + get() = _wifiAutoDownload ?: true + + val isEnableAutoDownload: Boolean + get() = _wifiAutoDownload != null + + val jpushId: String + get() = _jpushId ?: "" + + companion object { + + private const val ON = "on" + } + + @Parcelize + class SmsConfig( + @SerializedName("notice") + private var _notice: Boolean? = null, + @SerializedName("mobile") + private val _mobile: String? = null, + @SerializedName("mobile_validated") + private var _mobileValidated: Boolean? = null + ) : Parcelable { + + + var notice: Boolean + get() = _notice ?: false + set(value) { + _notice = value + } + + val mobile: String + get() = _mobile ?: "" + + var mobileValidated: Boolean + get() = _mobileValidated ?: false + set(value) { + _mobileValidated = value + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeRequest.kt b/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeRequest.kt new file mode 100644 index 0000000000..04a4082c4b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeRequest.kt @@ -0,0 +1,12 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +class ValidateCodeRequest( + @SerializedName("mobile") + val mobile: String, + @SerializedName("service_id") + val serviceId: String, + @SerializedName("code") + val code: String +) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeResponse.kt b/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeResponse.kt new file mode 100644 index 0000000000..531d43025f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ValidateCodeResponse.kt @@ -0,0 +1,15 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +class ValidateCodeResponse( + @SerializedName("service_id") + private val _serviceId: String? = null +) : Parcelable { + + val serviceId: String + get() = _serviceId ?: "" +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index 1361c09414..6f038a0611 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -1853,7 +1853,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable { mGameEntity, callback = getCallback(), - ) + ) } else { if ("download" == mGameEntity?.reserveStatus) { ReservationHelper.showDeleteReservationDialog(requireContext()) { @@ -1863,7 +1863,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable { } } } else { - ReservationHelper.showCancelReservationDialog(requireContext()) { + ReservationHelper.showCancelReservationDialog(requireContext(), mGameEntity) { ReservationHelper.cancelReservation(mGameEntity!!) { DetailDownloadUtils.updateViewHolder(detailViewHolder) showReserveBtn(isShowReserveBtn()) diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt index 939404a3b2..92865f9efa 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt @@ -122,6 +122,9 @@ class MyFollowedGameAdapter(context: Context, var mViewModel: MyFollowedGameView ) GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, mEntrance, exposureEvent) } + + binding.gameItemIncluded.tvAutoDownloadTips.goneIf(true) + binding.gameItemIncluded.ivEdit.goneIf(true) } } is FooterViewHolder -> { diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt index 937963ae37..c51e05448a 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivity.kt @@ -4,64 +4,175 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.Gravity -import android.view.MenuItem import android.view.View import android.widget.LinearLayout -import androidx.fragment.app.Fragment +import android.widget.TextView +import androidx.activity.viewModels +import androidx.constraintlayout.widget.Group +import androidx.lifecycle.Observer +import androidx.viewpager.widget.ViewPager +import com.gh.common.pop.BatchManagementPop import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R -import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout.PAGE_INDEX +import com.gh.gamecenter.common.base.adapter.FragmentAdapter +import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.utils.showRegulationTestDialogIfNeeded -import com.gh.gamecenter.common.utils.tryCatchInRelease -import com.gh.gamecenter.common.utils.updateStatusBarColor +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.BugFixedPopupWindow +import com.gh.gamecenter.common.view.TabIndicatorView import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.databinding.PopupMyGameGuideBinding import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import com.google.android.material.tabs.TabLayout /** * 我的游戏 */ -class MyGameActivity : BaseActivity_TabLayout() { +class MyGameActivity : BaseActivity() { + + private val viewModel by viewModels() + + private var checkedIndex = 0 + + private val titles by lazy { + listOf(R.string.played.toResString(), R.string.follow.toResString(), R.string.reserve.toResString()) + } + + private val batchManagePop by lazy { + BatchManagementPop.create( + this, + object : BatchManagementPop.OnBatchManagementListener { + + override fun enableAutoDownload() { + viewModel.changePageStatus(MyReservationFragment.ReservePageState.EnableAutoDownload) + } + + override fun batchManage() { + viewModel.changePageStatus(MyReservationFragment.ReservePageState.CancelReserve) + + } + }) + } + + private lateinit var vBack: View + private lateinit var vGameCollection: View + private lateinit var gGameCollection: Group + private lateinit var vMore: View + private lateinit var tvCancel: TextView + private lateinit var gMore: Group + private lateinit var viewPager: ViewPager + + override fun getLayoutId(): Int = R.layout.activity_my_game override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setNavigationTitle("我的游戏") - setToolbarMenu(R.menu.menu_my_game) - mDividerLineView?.visibility = View.VISIBLE - showGuide() updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface) - } - override fun initFragmentList(fragments: MutableList?) { - fragments?.add(MyPlayedGameFragment()) - fragments?.add(MyFollowedGameFragment()) - fragments?.add(MyReservationFragment()) - } + checkedIndex = intent.getIntExtra(PAGE_INDEX, 0) + vBack = findViewById(R.id.v_back) + vGameCollection = findViewById(R.id.v_game_collection) + gGameCollection = findViewById(R.id.g_game_collection) + vMore = findViewById(R.id.v_more) + tvCancel = findViewById(R.id.tv_cancel) + gMore = findViewById(R.id.g_more) + viewPager = findViewById(R.id.activity_view_pager) - override fun initTabTitleList(tabTitleList: MutableList?) { - tabTitleList?.add("玩过") - tabTitleList?.add("关注") - tabTitleList?.add("预约") - } - - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - MtaHelper.onEvent("我的光环", "我的游戏", "${mTabTitleList[position]}Tab") - MtaHelper.onEvent("我的光环_新", "我的游戏", "${mTabTitleList[position]}Tab") - NewFlatLogUtils.logMyGameTabClick(mTabTitleList[position]) - } - - override fun onMenuItemClick(item: MenuItem?): Boolean { - if (item?.itemId == R.id.menu_create_game_collection) { + vBack.setOnClickListener { finish() } + vGameCollection.setOnClickListener { showRegulationTestDialogIfNeeded { startActivity(GameCollectionEditActivity.getCreateIntent(this, mEntrance, "我的游戏")) } } - return super.onMenuItemClick(item) + vMore.setOnClickListener { + showBatchManagementPop() + } + + tvCancel.setOnClickListener { + viewModel.changePageStatus(MyReservationFragment.ReservePageState.Normal) + } + + initFragmentList() + + with(viewModel) { + reservePageStatus.observe(this@MyGameActivity, Observer { + val tabPosition = viewPager.currentItem + if (tabPosition == RESERVATION_INDEX) { + if (it is MyReservationFragment.ReservePageState.Normal) { + setViewsVisible(true, gGameCollection, gMore) + setViewsVisible(false, tvCancel) + } else { + setViewsVisible(false, gGameCollection, gMore) + setViewsVisible(true, tvCancel) + } + } else { + setViewsVisible(true, gGameCollection) + setViewsVisible(false, gMore, tvCancel) + } + }) + } + } + + private fun setViewsVisible(isVisible: Boolean, vararg views: View) { + views.forEach { + it.goneIf(!isVisible) + } + } + + + fun initFragmentList() { + val tabLayout = findViewById(R.id.activity_tab_layout) + val tabIndicatorView = findViewById(R.id.activity_tab_indicator) + + val fragments = listOf(MyPlayedGameFragment(), MyFollowedGameFragment(), MyReservationFragment()) + val pageAdapter = FragmentAdapter(supportFragmentManager, fragments, titles) + viewPager.offscreenPageLimit = fragments.size + viewPager.adapter = pageAdapter + viewPager.setCurrentItem(checkedIndex) + + tabLayout.setupWithViewPager(viewPager) + + tabIndicatorView.setupWithTabLayout(tabLayout) + tabIndicatorView.setupWithViewPager(viewPager) + tabIndicatorView.setIndicatorWidth(20) + + for (i in 0 until tabLayout.tabCount) { + val tab: TabLayout.Tab = tabLayout.getTabAt(i) ?: continue + val tabTitle = if (tab.text != null) tab.text.toString() else "" + val tabView = BaseFragment_TabLayout.createDefaultTabCustomView(this, tabTitle) + tab.setCustomView(tabView) + } + BaseFragment_TabLayout.initTabStyle(tabLayout, checkedIndex) + + viewPager.addOnPageChangeListener(::onPageSelected) + + onPageSelected(checkedIndex) + + showGuide(checkedIndex) + } + + fun onPageSelected(position: Int) { + MtaHelper.onEvent("我的光环", "我的游戏", "${titles[position]}Tab") + MtaHelper.onEvent("我的光环_新", "我的游戏", "${titles[position]}Tab") + NewFlatLogUtils.logMyGameTabClick(titles[position]) + + if (position == RESERVATION_INDEX) { + gMore.goneIf(false) + } else { + gMore.goneIf(true) + } + // 用户切换了tab:退出编辑模式 + viewModel.changePageStatus(MyReservationFragment.ReservePageState.Normal) + } + + private fun showBatchManagementPop() { + if (!batchManagePop.isShowing) { + batchManagePop.showAsDropDown(vMore, 0, (-4F).dip2px()) + } + } override fun isAutoResetViewBackgroundEnabled() = true @@ -69,10 +180,9 @@ class MyGameActivity : BaseActivity_TabLayout() { override fun onDarkModeChanged() { super.onDarkModeChanged() updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface) - getMenuItem(R.id.menu_create_game_collection).setIcon(R.drawable.ic_create_game_collection) } - private fun showGuide() { + private fun showGuide(position: Int) { AppExecutor.uiExecutor.executeWithDelay({ tryCatchInRelease { if (!SPUtils.getBoolean(Constants.SP_MY_GAME_GUIDE)) { @@ -86,11 +196,18 @@ class MyGameActivity : BaseActivity_TabLayout() { SPUtils.setBoolean(Constants.SP_MY_GAME_GUIDE, true) popupWindow.dismiss() } + + val paddingEnd = if (position == RESERVATION_INDEX) { + 48F.dip2px() + } else { + 0 + } + binding.guideContainer.setPadding(0, 0, paddingEnd, 0) popupWindow.run { isTouchable = true isFocusable = true if (!isFinishing) { - showAtLocation(window.decorView, Gravity.TOP, 0, 0) + showAtLocation(vGameCollection, Gravity.TOP, 0, 0) } } } @@ -98,6 +215,15 @@ class MyGameActivity : BaseActivity_TabLayout() { }, 500) } + override fun handleBackPressed(): Boolean { + if (viewModel.reservePageStatus.value == MyReservationFragment.ReservePageState.Normal) { + return super.handleBackPressed() + } else { + viewModel.changePageStatus(MyReservationFragment.ReservePageState.Normal) + return true + } + } + companion object { const val PLAYED_INDEX = 0 const val FOLLOWED_INDEX = 1 diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivityViewModel.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivityViewModel.kt new file mode 100644 index 0000000000..75abbfcdb5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyGameActivityViewModel.kt @@ -0,0 +1,17 @@ +package com.gh.gamecenter.mygame + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.livedata.Event + +class MyGameActivityViewModel : ViewModel() { + + private val _reservePageStatus = MutableLiveData() + val reservePageStatus: LiveData = _reservePageStatus + fun changePageStatus(state: MyReservationFragment.ReservePageState) { + if (state != reservePageStatus.value) { + _reservePageStatus.value = state + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt index 00640919f1..63c7435796 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt @@ -3,9 +3,13 @@ package com.gh.gamecenter.mygame import android.content.Context import android.util.SparseArray import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.RecyclerView import com.gh.common.databind.BindingAdapters import com.gh.common.exposure.IExposable +import com.gh.common.pop.CancelReservePop +import com.gh.common.pop.ReserveAllSelectPop import com.gh.common.util.DownloadItemUtils import com.gh.common.util.NewFlatLogUtils import com.gh.common.util.ReservationHelper @@ -17,23 +21,64 @@ import com.gh.gamecenter.common.callback.CancelListener import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.common.viewholder.FooterViewHolder -import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.EmptyCallback -import com.gh.gamecenter.core.utils.MtaHelper -import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.databinding.ItemFollowedGameBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.game.GameAndPosition +import com.lightgame.view.CheckableImageView class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewModel) : ListAdapter(context), IExposable { + private var pageState: MyReservationFragment.ReservePageState = MyReservationFragment.ReservePageState.Normal + private val isEditing: Boolean + get() = pageState != MyReservationFragment.ReservePageState.Normal + private val mEntrance = "(我的预约)" private val mExposureEventSparseArray: SparseArray = SparseArray() + private val selectedGames = mutableSetOf() + + private val allSelectPop by lazy { + ReserveAllSelectPop.create(mContext, object : ReserveAllSelectPop.OnReserveAllSelectListener { + override fun selectAll(isChecked: Boolean): Int { + selectedGames.clear() + if (isChecked) { + if (pageState == MyReservationFragment.ReservePageState.EnableAutoDownload) { + mEntityList + .filter { + it.wifiAutoDownloadEnable && !it.wifiAutoDownload + } + .let(selectedGames::addAll) + } else { + mEntityList.let(selectedGames::addAll) + } + + } + notifyItemRangeChanged(0, mEntityList.size) + return selectedGames.size + } + + override fun submit() { + doSubmit() + + } + }) + } + + private fun doSubmit() { + if (pageState == MyReservationFragment.ReservePageState.EnableAutoDownload) { + allSelectPop.dismiss() + mViewModel.enableAutoDownload(true, selectedGames.toSet()) + mViewModel.changeReserveStatus(MyReservationFragment.ReservePageState.Normal) + } else { + showCancelReservationInBatchDialog(selectedGames.toSet()) + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { @@ -48,6 +93,7 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo ) ) } + else -> { FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false)) } @@ -103,6 +149,24 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo gameDes.text = gameEntity.decoratedDes recommendStar.rating = gameEntity.recommendStar.toFloat() GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv) + tvAutoDownloadTips.goneIf(!gameEntity.wifiAutoDownload) + ivEdit.goneIf(isEditing) + if (isEditing) { + selectIv.goneIf(false) + updateSelectedStatus(selectIv, gameEntity) + cardContainer.updateLayoutParams { + if (this is MarginLayoutParams) { + marginStart = 120F.dip2px() + } + } + } else { + selectIv.goneIf(true) + cardContainer.updateLayoutParams { + if (this is MarginLayoutParams) { + marginStart = 92F.dip2px() + } + } + } } } @@ -115,8 +179,19 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo ) mExposureEventSparseArray.append(position, exposureEvent) + val gameItemBinding = holder.binding.gameItemIncluded DownloadItemUtils.updateItemWithReserveStatus( - GameViewHolder(holder.binding.gameItemIncluded), + GameViewHolder(holder.binding.gameItemIncluded.root).apply { + gameDownloadBtn = gameItemBinding.downloadBtn + gameDes = gameItemBinding.gameDes + gameDownloadTips = gameItemBinding.downloadTipsLottie + multiVersionDownloadTv = gameItemBinding.multiVersionDownloadTv + gameRating = gameItemBinding.gameRating + recommendContainer = gameItemBinding.recommendContainer + recommendIv = gameItemBinding.recommendIv + recommendTv = gameItemBinding.recommendTv + recommendStarInfo = gameItemBinding.recommendStarInfo + }, gameEntity ) @@ -129,7 +204,35 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo gameEntity.id, gameEntity.name ?: "" ) - GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, mEntrance, exposureEvent) + if (pageState == MyReservationFragment.ReservePageState.Normal) { + GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, mEntrance, exposureEvent) + } else { + val isEnableSelect = pageState == MyReservationFragment.ReservePageState.CancelReserve + || (gameEntity.wifiAutoDownloadEnable && !gameEntity.wifiAutoDownload) + if (isEnableSelect) { // 可勾选状态 + val ivSelect = holder.binding.gameItemIncluded.selectIv + if (ivSelect.isEnabled) { + ivSelect.toggle() + if (ivSelect.isChecked) { + selectedGames.add(gameEntity) + } else { + selectedGames.remove(gameEntity) + } + updateSelectedStatus(ivSelect, gameEntity) + allSelectPop.updateSelectedCount(selectedGames.size) + } + } else { // 不可勾选状态,弹出相应的提示 + val toastResId = if (gameEntity.wifiAutoDownload) { + // 已经开启wifi自动下载 + R.string.has_enabled_for_auto_download_in_wifi + } else { + R.string.not_support_auto_download_in_wifi + } + ToastUtils.showToast(toastResId.toResString()) + } + + } + } if ("appointment" == gameEntity.reserveStatus) { @@ -149,11 +252,24 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo ) } initDownloadButtonOffset(holder.binding) - holder.binding.root.setOnLongClickListener { - showCancelReservationDialog(gameEntity) - true + + holder.binding.gameItemIncluded.ivEdit.setOnClickListener { + CancelReservePop.create( + gameEntity, + mContext, + object : CancelReservePop.OnCancelReserveListener { + override fun enableAutoDownload(isEnable: Boolean) { + mViewModel.enableAutoDownload(isEnable, setOf(gameEntity)) + } + + override fun cancelReserve() { + showCancelReservationDialog(gameEntity) + } + + }).showAsDropDown(holder.binding.gameItemIncluded.ivEdit) } } + is FooterViewHolder -> { holder.initItemPadding() holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) @@ -161,6 +277,34 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo } } + private fun updateSelectedStatus(selectIv: CheckableImageView, gameEntity: GameEntity) { + if (pageState == MyReservationFragment.ReservePageState.CancelReserve) { + selectIv.isChecked = selectedGames.any { gameEntity.id == it.id } + val isChecked = selectedGames.any { gameEntity.id == it.id } + if (isChecked) { + selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_selected) + } else { + selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_default) + } + } else { // 批量开启wifi自动下载 + when { + !gameEntity.wifiAutoDownloadEnable -> { // 此游戏不支持wifi自动下载,不可勾选状态 + selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_unable) + } + + gameEntity.wifiAutoDownload -> {// 已经开启了wifi自动下载,勾选-不可编辑状态 + selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_selected_unable) + } + + else -> { // 默认状态,可勾选 + selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(mContext)) + selectIv.isChecked = selectedGames.any { gameEntity.id == it.id } + } + } + + } + } + fun notifyItemAndRemoveDownload(status: EBDownloadStatus) { val data = getGameEntityByPackage(status.packageName) for (gameAndPosition in data) { @@ -192,17 +336,9 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo } } - private fun showDeleteReservationDialog(game: GameEntity) { - ReservationHelper.showDeleteReservationDialog(mContext, object : EmptyCallback { - override fun onCallback() { - mViewModel.deleteReservation(game) - } - }) - } - private fun showCancelReservationDialog(game: GameEntity) { NewFlatLogUtils.logMyGameReserveTabGameCardClick("按钮", game.id, game.name ?: "") - ReservationHelper.showCancelReservationDialog(mContext, { + ReservationHelper.showCancelReservationDialog(mContext, game, { NewFlatLogUtils.logMyGameCancelReserveDialogClick("确定取消", game.id, game.name ?: "") mViewModel.cancelReservation(game) }, object : CancelListener { @@ -212,8 +348,67 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo }) } + private fun showCancelReservationInBatchDialog(games: Set) { + // 批量删除,埋点在外部上报 + games.forEach { + SensorsBridge.trackCancelAppointmentDialogShow(it.id, it.name ?: "", it.categoryChinese) + } + ReservationHelper.showCancelReservationDialog(mContext, null, { + games.forEach { + SensorsBridge.trackCancelAppointmentDialogClick(it.id, it.name ?: "", it.categoryChinese, "确定取消") + } + mViewModel.cancelReserveInBatch(games) + if (allSelectPop.isShowing) { + allSelectPop.dismiss() + } + mViewModel.changeReserveStatus(MyReservationFragment.ReservePageState.Normal) + }, object : CancelListener { + override fun onCancel() { + games.forEach { + SensorsBridge.trackCancelAppointmentDialogClick( + it.id, + it.name ?: "", + it.categoryChinese, + "暂不取消" + ) + } + } + }, dialogCancelCallback = { + games.forEach { + SensorsBridge.trackCancelAppointmentDialogClick(it.id, it.name ?: "", it.categoryChinese, "关闭弹窗") + } + }) + } + override fun getEventByPosition(pos: Int): ExposureEvent = mExposureEventSparseArray.get(pos) override fun getEventListByPosition(pos: Int) = null + fun changeStatus(status: MyReservationFragment.ReservePageState) { + if (pageState != status) { + selectedGames.clear() + pageState = status + notifyItemRangeChanged(0, itemCount) + + if (status == MyReservationFragment.ReservePageState.Normal) { + allSelectPop.dismiss() + } else { + allSelectPop.show(status, mContext) + + } + } + } + + fun updatePopupWindow() { + with(allSelectPop.binding) { + cbAll.setButtonDrawable(R.drawable.selector_kaifu_report) + cbAll.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext)) + tvNumber.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext)) + tvNumber.background = com.gh.gamecenter.common.R.drawable.bg_common_button_fill_gradient_blue.toDrawable(mContext) + tvSubmit.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(mContext)) + root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(mContext)) + vLine.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_divider.toColor(mContext)) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationFragment.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationFragment.kt index 0005098c16..7da579d0d1 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationFragment.kt @@ -4,12 +4,9 @@ import android.os.Bundle import android.text.TextUtils import android.view.View import androidx.core.content.ContextCompat +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.ExposureListener -import com.gh.gamecenter.common.utils.ifLogin -import com.gh.gamecenter.common.utils.toColor -import com.gh.gamecenter.common.utils.toDrawable -import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.common.view.CustomDividerItemDecoration import com.gh.download.DownloadManager import com.gh.gamecenter.R @@ -21,6 +18,9 @@ import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.common.eventbus.EBReuse +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.livedata.EventObserver import com.gh.gamecenter.login.user.UserManager import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -34,6 +34,8 @@ class MyReservationFragment : ListFragment() private var mExposureListener: ExposureListener? = null private val mBinding by lazy { FragmentListBaseBinding.inflate(layoutInflater) } + private val activityViewModel by activityViewModels() + private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { val data = mAdapter?.getGameEntityByPackage(downloadEntity.packageName) ?: arrayListOf() @@ -57,6 +59,27 @@ class MyReservationFragment : ListFragment() mBinding.reuseNoneData.reuseNoneDataTv.setOnClickListener(this) mListRv.addOnScrollListener(mExposureListener!!) mListRv.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())) + + with(activityViewModel) { + reservePageStatus.observe(viewLifecycleOwner) { + mAdapter?.changeStatus(it) + } + } + + with(mViewModel) { + enableAutoDownloadSuccessfully.observe(viewLifecycleOwner, EventObserver { + if (it) { + ToastUtils.showToast(R.string.has_enabled_for_auto_download_in_wifi.toResString()) + } else { + ToastUtils.showToast(R.string.has_unable_for_auto_download_in_wifi.toResString()) + } + + }) + + changePageStateAction.observe(viewLifecycleOwner, EventObserver { + activityViewModel.changePageStatus(it) + }) + } } override fun onStart() { @@ -153,5 +176,15 @@ class MyReservationFragment : ListFragment() override fun onDarkModeChanged() { super.onDarkModeChanged() mListRv.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())) + mAdapter?.updatePopupWindow() + } + + sealed class ReservePageState { + + object Normal : ReservePageState() // 正常显示状态 + + object EnableAutoDownload : ReservePageState() // 批量启动自动下载状态 + + object CancelReserve : ReservePageState() // 批量管理-取消预约状态 } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationViewModel.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationViewModel.kt index 9b8a67a770..a821c858e9 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationViewModel.kt @@ -2,20 +2,31 @@ package com.gh.gamecenter.mygame import android.annotation.SuppressLint import android.app.Application +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.alibaba.android.arouter.launcher.ARouter import com.gh.common.repository.ReservationRepository +import com.gh.common.util.ReservationHelper.getReserveRequestBody import com.gh.gamecenter.core.runOnUiThread -import com.gh.gamecenter.common.utils.createRequestBody import com.gh.download.DownloadManager import com.gh.gamecenter.common.baselist.ListViewModel import com.gh.gamecenter.common.baselist.LoadType +import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.login.user.UserManager import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.common.utils.toRequestBody +import com.gh.gamecenter.core.provider.IPushProvider +import com.gh.gamecenter.livedata.Event import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.halo.assistant.fragment.reserve.ReserveReminderRepository import com.lightgame.utils.Utils import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody @@ -23,13 +34,16 @@ class MyReservationViewModel(application: Application) : ListViewModel() // key: packageName + position, value: position + private val repository = ReserveReminderRepository.newInstance() + private val compositeDisposable = CompositeDisposable() + override fun provideDataObservable(page: Int): Observable>? { return null } override fun provideDataSingle(page: Int): Single> { - return RetrofitManager.getInstance().api - .getGameReservations(UserManager.getInstance().userId, page, Utils.getTime(getApplication())) + return RetrofitManager.getInstance().newApi + .getGameReservations(page, Utils.getTime(getApplication())) } override fun mergeResultLiveData() { @@ -53,27 +67,19 @@ class MyReservationViewModel(application: Application) : ListViewModel() - requestMap["game_id"] = game.id - val single = if (deleteReservation) { - RetrofitManager.getInstance().api - .deleteGameReservation(requestMap.createRequestBody()) + RetrofitManager.getInstance().newApi + .deleteGameReservation(game.id, getReserveRequestBody(game, HaloApp.getInstance().application)) .subscribeOn(Schedulers.io()) } else { - RetrofitManager.getInstance().api - .cancelGameReservation(requestMap.createRequestBody()) + RetrofitManager.getInstance().newApi + .cancelGameReservation(game.id, getReserveRequestBody(game, HaloApp.getInstance().application)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } @@ -105,4 +111,94 @@ class MyReservationViewModel(application: Application) : ListViewModel>() + val enableAutoDownloadSuccessfully: LiveData> = _enableAutoDownloadSuccessfully + fun enableAutoDownload(enable: Boolean, games: Set) { + if (enable) { + games.forEach { + SensorsBridge.trackOpenAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese) + } + } else { + games.forEach { + SensorsBridge.trackCancelAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese) + } + } + val params = createParamsInBatch(games).apply { + put("wifi_auto_download", enable) + } + repository.enableAutoDownloadInBatches(params.toRequestBody()) + .map { + val oldList = mResultLiveData.value ?: emptyList() + val newList = oldList.map { item -> + if (games.any { it.id == item.id }) { + item.copy(_wifiAutoDownload = enable) + } else { + item + } + + } + newList + } + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + mResultLiveData.value = data + _enableAutoDownloadSuccessfully.value = Event(enable) + } + + }).let(compositeDisposable::add) + } + + fun cancelReserveInBatch(games: Set) { + val params = createParamsInBatch(games).apply { + put("appointment", false) + } + repository.cancelReserveInBatch(params.toRequestBody()) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + ReservationRepository.refreshReservations() + val oldList = mResultLiveData.value ?: emptyList() + val newList = oldList.toMutableList() + + newList.removeAll { + games.any { game -> game.id == it.id } + } + if (newList.isEmpty()) { + // 刷新页面 + runOnUiThread { load(LoadType.REFRESH) } + } else { + mResultLiveData.postValue(newList) + } + + } + + }).let(compositeDisposable::add) + } + + private fun createParamsInBatch(games: Set): HashMap { + val jPushId = + (ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider) + ?.getRegistrationId(HaloApp.getInstance().application) ?: "" + val idAndMirrorType = games.map { + var mirrorPosition = it.getMirrorPosition() + if (mirrorPosition == -1) { + mirrorPosition = 0 + } + hashMapOf("_id" to it.id, "mirror_type" to mirrorPosition) + } + return hashMapOf("jpush_id" to jPushId, "games" to idAndMirrorType) + } + + private val _changePageStateAction = MutableLiveData>() + val changePageStateAction: LiveData> = _changePageStateAction + fun changeReserveStatus(state: MyReservationFragment.ReservePageState) { + _changePageStateAction.value = Event(state) + } + + override fun onCleared() { + super.onCleared() + compositeDisposable.clear() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 31732f07a5..9ab1bc95dd 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -5,7 +5,6 @@ import com.gh.gamecenter.common.entity.LaunchRedirectWrapper; import com.gh.gamecenter.common.entity.LinkEntity; import com.gh.gamecenter.common.entity.OssEntity; import com.gh.gamecenter.common.entity.SignatureEntity; -import com.gh.gamecenter.common.entity.SimpleGameEntity; import com.gh.gamecenter.common.entity.ToolBoxEntity; import com.gh.gamecenter.common.entity.WechatConfigEntity; import com.gh.gamecenter.entity.ActivityLabelEntity; @@ -72,6 +71,8 @@ import com.gh.gamecenter.entity.RatingComment; import com.gh.gamecenter.entity.RatingDraftEntity; import com.gh.gamecenter.entity.RatingReplyEntity; import com.gh.gamecenter.entity.RecommendPopupEntity; +import com.gh.gamecenter.entity.ReserveModifyEntity; +import com.gh.gamecenter.entity.ReserveReminderEntity; import com.gh.gamecenter.entity.SearchSubjectEntity; import com.gh.gamecenter.entity.ServerPublishEntity; import com.gh.gamecenter.entity.ServerSubscriptionEntity; @@ -90,6 +91,8 @@ import com.gh.gamecenter.entity.TimeEntity; import com.gh.gamecenter.entity.ToolBoxBlockEntity; import com.gh.gamecenter.entity.UnifiedUserTrendEntity; import com.gh.gamecenter.entity.UserAuthEntity; +import com.gh.gamecenter.entity.ValidateCodeRequest; +import com.gh.gamecenter.entity.ValidateCodeResponse; import com.gh.gamecenter.entity.VersionVoteEntity; import com.gh.gamecenter.entity.VideoDataOverViewEntity; import com.gh.gamecenter.entity.VideoDraftEntity; @@ -117,6 +120,7 @@ import com.gh.gamecenter.feature.entity.MessageUnreadEntity; import com.gh.gamecenter.feature.entity.NewsEntity; import com.gh.gamecenter.feature.entity.PersonalEntity; import com.gh.gamecenter.feature.entity.QuestionDraftEntity; +import com.gh.gamecenter.feature.entity.ReserveOnlineEntity; import com.gh.gamecenter.feature.entity.ServerCalendarEntity; import com.gh.gamecenter.feature.entity.ServerCalendarFormEntity; import com.gh.gamecenter.feature.entity.ServerCalendarGame; @@ -141,6 +145,7 @@ import com.gh.gamecenter.qa.entity.QuestionsIndexEntity; import com.gh.gamecenter.qa.entity.TopCommunityCategory; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.protobuf.Any; import java.util.ArrayList; import java.util.HashMap; @@ -1426,26 +1431,26 @@ public interface ApiService { /** * 新建游戏预约 */ - @POST("./appointment:apply") - Single createNewGameReservation(@Body RequestBody body); + @POST("games/{game_id}/appointment") + Single createNewGameReservation(@Path("game_id") String gameId, @Body RequestBody body); /** * 取消游戏预约 */ - @POST("./appointment:cancel") - Single cancelGameReservation(@Body RequestBody body); + @POST("games/{game_id}/appointment/cancel") + Single cancelGameReservation(@Path("game_id") String gameId, @Body RequestBody body); /** * 删除游戏预约 */ - @POST("./appointment:delete") - Single deleteGameReservation(@Body RequestBody body); + @DELETE("games/{game_id}/appointment") + Single deleteGameReservation(@Path("game_id") String gameId, @Body RequestBody body); /** * 获取用户游戏预约列表 */ - @GET("users/{user_id}/appointment") - Single> getGameReservations(@Path("user_id") String userId, @Query("page") int page, @Query("timestamp") long timestamp); + @GET("users/appointments") + Single> getGameReservations(@Query("page") int page, @Query("timestamp") long timestamp); /** * 获取用户游戏预约列表 @@ -1769,13 +1774,13 @@ public interface ApiService { /** * 绑定微信 */ - @POST("./wechat:bind") + @POST("users/wechat/bind") Single postBindWechat(@Body RequestBody body); /** * 获取公众号配置信息 */ - @GET("wechat/config") + @GET("users/wechat/config") Single getWechatConfig(); /** @@ -1842,10 +1847,25 @@ public interface ApiService { Observable deleteVideo(@Path("video_id") String videoId); /** - * 获取预约弹窗 + * 获取预约上线弹窗 */ - @GET("users/{user_id}/appointment_popup") - Single> getReserveDialog(@Path("user_id") String userId); + @GET("users/appointment_popup") + Single getReserveDialog(); + + /** + * 预约游戏上线弹窗-游戏列表(开启wifi自动下载) + */ + @GET("users/appointment_popup/games?filter=wifi_auto_download:true") + Single> loadGamesWithAutoDownload( + @Query("page") int page, + @Query("page_size") int page_size, + @Query("filter") String filter); + + /** + * 添加下载任务成功 + */ + @POST("users/appointments/games/log_download") + Single logAutoDownload(@Body HashMap body); /** * 获取我的光环部分功能的未读数 @@ -3360,6 +3380,30 @@ public interface ApiService { @GET("game_lists/hot_columns") Single> getHotColumns(); + + @PATCH("games/{game_id}/appointment") + Single reserveModify(@Path("game_id") String gameId, @Body RequestBody body); + + /** + * 获取验证码 + */ + @POST("mobile/validate/code") + Single getValidateCode(@Body HashMap map); + + /** + * 验证手机号 + */ + @POST("mobile/validate") + Single mobileValidate(@Body ValidateCodeRequest request); + + /** + * 批量操作: + * 1.是/否 开启wifi自动下载 + * 2.取消预约 + */ + @POST("users/appointments/settings") + Single reserveSetting(@Body RequestBody body); + /** * 分流器列表信息 */ diff --git a/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderFragment.kt b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderFragment.kt new file mode 100644 index 0000000000..21fce3524e --- /dev/null +++ b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderFragment.kt @@ -0,0 +1,257 @@ +package com.halo.assistant.fragment.reserve + +import android.content.Context +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.gh.common.dialog.ReserveReminderPhoneNumberDialog +import com.gh.common.dialog.ReserveReminderPhoneNumberDialog.Companion.DIALOG_TYPE_BIND_PHONE +import com.gh.common.dialog.ReserveReminderPhoneNumberDialog.Companion.DIALOG_TYPE_CHANGE_PHONE +import com.gh.common.dialog.ReserveReminderPhoneNumberDialog.Companion.DIALOG_TYPE_VERIFY_PHONE +import com.gh.common.dialog.ReserveSuccessReminderDialog +import com.gh.gamecenter.ShellActivity +import com.gh.gamecenter.ShellActivity.Companion.getIntent +import com.gh.gamecenter.WebActivity +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.WechatConfigEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.toObject +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.ReserveReminderEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.livedata.EventObserver + +class ReserveReminderContainerFragment : Fragment(), OnReserveReminderListener { + + private var isWechatBinding = false + + private var isCanDismissed = true + + private val repository by lazy { + ReserveReminderRepository.newInstance() + } + + private val viewModel by viewModels() + + private lateinit var reserveReminderEntity: ReserveReminderEntity + private var game: GameEntity? = null + + private val reservesReminderDialog by lazy { + ReserveSuccessReminderDialog.create(requireContext(), this).apply { + setOnDismissListener { + if (isCanDismissed) { + onDismiss() + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + game = arguments?.getParcelable(KEY_GAME) + reserveReminderEntity = + arguments?.getParcelable(KEY_RESERVE_REMINDER) ?: ReserveReminderEntity() + + viewModel.updateReserveReminder(reserveReminderEntity) + reservesReminderDialog.show() + + with(viewModel) { + val lifeCycleOwner = this@ReserveReminderContainerFragment + reserveReminder.observe(lifeCycleOwner) { + reservesReminderDialog.updateReserveReminder(it) + } + + sendVerifyCode.observe(lifeCycleOwner, EventObserver { + showPhoneNumberDialog(DIALOG_TYPE_VERIFY_PHONE, reserveReminderEntity.smsConfig.mobile, it) + }) + + unableSmsReminderSuccessfully.observe(lifeCycleOwner, EventObserver { + reserveReminderEntity.smsConfig.notice = false + reserveReminderEntity.smsConfig.mobileValidated = false + viewModel.updateReserveReminder(reserveReminderEntity) + }) + } + } + + override fun updateSmsReminder(isEnable: Boolean) { + if (isEnable) { + trackAppointmentSuccessDialogClick("开启短信提醒") + showPhoneNumberDialog(DIALOG_TYPE_BIND_PHONE, reserveReminderEntity.smsConfig.mobile) + } else { + trackAppointmentSuccessDialogClick("关闭短信提醒") + viewModel.unableSmsReminder(game?.id ?: "") + } + + } + + override fun updateWechatReminder() { + isWechatBinding = true + trackAppointmentSuccessDialogClick("开启微信提醒") + startActivity(WebActivity.getBindWechatIntent(requireContext())) + } + + override fun changedPhoneNumber() { + trackAppointmentSuccessDialogClick("修改手机号") + showPhoneNumberDialog(DIALOG_TYPE_CHANGE_PHONE, reserveReminderEntity.smsConfig.mobile) + } + + override fun verifyPhoneNumber() { + trackAppointmentSuccessDialogClick("验证手机号") + viewModel.sendVerifyCode(reserveReminderEntity.smsConfig.mobile) + } + + override fun changeWechatBinding() { + isWechatBinding = true + trackAppointmentSuccessDialogClick("更换绑定微信") + startActivity(WebActivity.getBindWechatIntent(requireContext())) + } + + override fun bindPhoneSuccessfully(hasValidated: Boolean, phone: String) { + reserveReminderEntity.smsConfig = + ReserveReminderEntity.SmsConfig(_notice = true, _mobile = phone, _mobileValidated = hasValidated) + + viewModel.updateReserveReminder(reserveReminderEntity) + } + + override fun phoneNumberValidateSuccessfully() { + reserveReminderEntity.smsConfig.mobileValidated = true + viewModel.updateReserveReminder(reserveReminderEntity) + } + + override fun enableAutoDownload(isEnable: Boolean) { + viewModel.enableAutoDownload(game, isEnable) + } + + override fun realName() { + trackAppointmentSuccessDialogClick("实名认证") + val intent = getIntent(requireContext(), ShellActivity.Type.REAL_NAME_INFO) + requireContext().startActivity(intent) + } + + + private fun showPhoneNumberDialog(dialogType: Int, phone: String, serviceId: String = "") { + isCanDismissed = false + reservesReminderDialog.dismiss() + ReserveReminderPhoneNumberDialog.create( + requireContext(), + dialogType, + phone, + serviceId, + game?.id ?: "", + repository, + this + ) + .let { + it.show() + it.setOnDismissListener { + isCanDismissed = true + reservesReminderDialog.show() + } + } + } + + override fun onResume() { + super.onResume() + if (isWechatBinding) { + isWechatBinding = false + val wechatConfig = SPUtils.getString(Constants.SP_WECHAT_CONFIG).toObject() + if (reserveReminderEntity.wechatConfig.bind != wechatConfig?.bind + || reserveReminderEntity.wechatConfig.notice != wechatConfig.notice + || reserveReminderEntity.wechatConfig.follow != wechatConfig.follow + || reserveReminderEntity.wechatConfig.nickName != wechatConfig.nickName + ) { + reserveReminderEntity.wechatConfig = wechatConfig ?: WechatConfigEntity() + viewModel.updateReserveReminder(reserveReminderEntity) + } + } + } + + + private fun onDismiss() { + SensorsBridge.trackAppointmentSuccessDialogClick( + game?.id ?: "", + game?.name ?: "", + game?.categoryChinese ?: "", + "关闭弹窗" + ) + val ft = parentFragmentManager.beginTransaction() + ft.remove(this) + ft.commitAllowingStateLoss() + } + + private fun trackAppointmentSuccessDialogClick( + buttonName: String, + wechatRemind: Boolean? = null, + messageRemind: Boolean? = null, + autoDownloadEnable: Boolean? = null + ) { + SensorsBridge.trackAppointmentSuccessDialogClick( + game?.id ?: "", + game?.name ?: "", + game?.categoryChinese ?: "", + buttonName, + wechatRemind, + messageRemind, + autoDownloadEnable + ) + } + + companion object { + + private const val KEY_RESERVE_REMINDER = "key_reserve_reminder" + private const val KEY_GAME = "key_game" + + @JvmStatic + fun show(context: Context, gameEntity: GameEntity?, reserveReminder: ReserveReminderEntity) { + (if (context is AppCompatActivity) { + context.supportFragmentManager + } else { + (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager + })?.let { + val tag = ReserveReminderContainerFragment::class.java.name + val fragment = it.findFragmentByTag(tag) ?: newInstance(gameEntity, reserveReminder) + if (!fragment.isAdded) { + it.beginTransaction() + .add(fragment, tag) + .commitAllowingStateLoss() + } + } + } + + private fun newInstance( + gameEntity: GameEntity?, + reserveReminder: ReserveReminderEntity + ): ReserveReminderContainerFragment { + return ReserveReminderContainerFragment().apply { + arguments = Bundle().apply { + putParcelable(KEY_GAME, gameEntity) + putParcelable(KEY_RESERVE_REMINDER, reserveReminder) + } + } + } + } +} + +interface OnReserveReminderListener { + + fun updateSmsReminder(isEnable: Boolean) + + fun updateWechatReminder() + + fun bindPhoneSuccessfully(hasValidated: Boolean, phone: String) + + fun changedPhoneNumber() + + fun verifyPhoneNumber() + + fun changeWechatBinding() + + fun phoneNumberValidateSuccessfully() + + fun enableAutoDownload(isEnable: Boolean) + + fun realName() +} \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderRepository.kt b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderRepository.kt new file mode 100644 index 0000000000..9376bffb3e --- /dev/null +++ b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderRepository.kt @@ -0,0 +1,102 @@ +package com.halo.assistant.fragment.reserve + +import android.annotation.SuppressLint +import android.os.Handler +import android.os.Looper +import android.os.Message +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.toRequestBody +import com.gh.gamecenter.entity.ReserveModifyEntity +import com.gh.gamecenter.entity.ReserveReminderEntity +import com.gh.gamecenter.entity.ValidateCodeRequest +import com.gh.gamecenter.entity.ValidateCodeResponse +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.retrofit.service.ApiService +import io.reactivex.Single +import io.reactivex.schedulers.Schedulers +import okhttp3.RequestBody +import okhttp3.ResponseBody + +class ReserveReminderRepository( + private val apiService: ApiService +) { + + private val handler = object : Handler(Looper.getMainLooper()) { + + @SuppressLint("CheckResult") + override fun handleMessage(msg: Message) { + if (msg.what == MESSAGE_WHAT_ENABLE_AUTO_DOWNLOAD) { + val content = msg.obj as? Pair ?: return + val (game, isEnable) = content + val body = hashMapOf( + "wifi_auto_download" to isEnable + ).toRequestBody() + + val buttonName = if (isEnable) { + "勾选自动下载" + } else { + "取消勾选自动下载" + } + SensorsBridge.trackAppointmentSuccessDialogClick( + game?.id ?: "", + game?.name ?: "", + game?.categoryChinese ?: "", + buttonName + ) + apiService.reserveModify(game?.id, body) + .subscribeOn(Schedulers.io()) + .subscribe({}, {}) + } + } + } + + fun sendVerifyCode(mobile: String): Single = + apiService.getValidateCode(hashMapOf("mobile" to mobile)) + + fun bindPhone(gameId: String, phone: String): Single { + val body = hashMapOf( + "sms_config" to ReserveReminderEntity.SmsConfig(true, phone) + ).toRequestBody() + return apiService.reserveModify(gameId, body) + } + + fun verifyPhoneNumber(phoneNumber: String, code: String, serviceId: String): Single { + val request = ValidateCodeRequest(phoneNumber, serviceId, code) + return apiService.mobileValidate(request) + } + + fun unableSmsReminder(gameId: String): Single { + val body = hashMapOf( + "sms_config" to hashMapOf("notice" to false) + ).toRequestBody() + return apiService.reserveModify(gameId, body) + } + + /** + * 为防止用户快速点击,这里300ms之内只会请求一次接口 + */ + fun enableAutoDownload(game: GameEntity?, enable: Boolean) { + val message = Message.obtain() + message.what = MESSAGE_WHAT_ENABLE_AUTO_DOWNLOAD + message.obj = game to enable + handler.removeMessages(MESSAGE_WHAT_ENABLE_AUTO_DOWNLOAD) + handler.sendMessageDelayed(message, 300) + } + + fun enableAutoDownloadInBatches(requestBody: RequestBody): Single { + return apiService.reserveSetting(requestBody) + } + + fun cancelReserveInBatch(requestBody: RequestBody): Single { + return apiService.reserveSetting(requestBody) + } + + companion object { + + private const val MESSAGE_WHAT_ENABLE_AUTO_DOWNLOAD = 1 + + fun newInstance() = + ReserveReminderRepository(RetrofitManager.getInstance().newApi) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderViewModel.kt b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderViewModel.kt new file mode 100644 index 0000000000..b8d3dc5dc6 --- /dev/null +++ b/app/src/main/java/com/halo/assistant/fragment/reserve/ReserveReminderViewModel.kt @@ -0,0 +1,68 @@ +package com.halo.assistant.fragment.reserve + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.entity.ReserveModifyEntity +import com.gh.gamecenter.entity.ReserveReminderEntity +import com.gh.gamecenter.entity.ValidateCodeResponse +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.livedata.Event +import io.reactivex.disposables.CompositeDisposable + +class ReserveReminderViewModel : ViewModel() { + + private val repository = ReserveReminderRepository.newInstance() + + private val composeDisposable = CompositeDisposable() + + private val _reserveReminder = MutableLiveData() + val reserveReminder: LiveData = _reserveReminder + fun updateReserveReminder(reserveReminder: ReserveReminderEntity) { + _reserveReminder.value = reserveReminder + } + + private val _sendVerifyCode = MutableLiveData>() + val sendVerifyCode: LiveData> = _sendVerifyCode + + fun sendVerifyCode(phone: String) { + repository.sendVerifyCode(phone) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ValidateCodeResponse) { + _sendVerifyCode.value = Event(data.serviceId) + } + + }).let(composeDisposable::add) + } + + private val _unableSmsReminderSuccessfully = MutableLiveData>() + val unableSmsReminderSuccessfully: LiveData> = _unableSmsReminderSuccessfully + fun unableSmsReminder(gameId: String) { + repository.unableSmsReminder(gameId) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ReserveModifyEntity) { + _unableSmsReminderSuccessfully.value = Event(Unit) + } + + }).let(composeDisposable::add) + } + + /** + * 由于可能存在用户点击了自动下载立马就退出dialog,这个方法仍然需要执行完毕 + * 所以这里当 预约提醒弹窗 退出时,仍然继续执行 + */ + fun enableAutoDownload(game: GameEntity?, enable: Boolean) { + repository.enableAutoDownload(game, enable) + } + + + override fun onCleared() { + composeDisposable.clear() + } + + +} \ No newline at end of file diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_online_reminder.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_online_reminder.webp new file mode 100644 index 0000000000..1a4cc83885 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_online_reminder.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_reminder_real_name.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_reminder_real_name.webp new file mode 100644 index 0000000000..34872d1082 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_reminder_real_name.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_success_top.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_success_top.webp new file mode 100644 index 0000000000..8ec492e7b3 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_reserve_success_top.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/section_more_background.png b/app/src/main/res/drawable-night-xxxhdpi/section_more_background.png deleted file mode 100755 index 1ebecc621f..0000000000 Binary files a/app/src/main/res/drawable-night-xxxhdpi/section_more_background.png and /dev/null differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/section_more_background.webp b/app/src/main/res/drawable-night-xxxhdpi/section_more_background.webp new file mode 100644 index 0000000000..d3bd1a6b6e Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/section_more_background.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_reserve_online_reminder.webp b/app/src/main/res/drawable-xxxhdpi/bg_reserve_online_reminder.webp new file mode 100644 index 0000000000..04b080f932 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_reserve_online_reminder.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_reserve_reminder_real_name.webp b/app/src/main/res/drawable-xxxhdpi/bg_reserve_reminder_real_name.webp new file mode 100644 index 0000000000..6a2bf9ecd5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_reserve_reminder_real_name.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_reserve_success_top.webp b/app/src/main/res/drawable-xxxhdpi/bg_reserve_success_top.webp new file mode 100644 index 0000000000..7926d57d63 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_reserve_success_top.webp differ diff --git a/app/src/main/res/drawable/bg_enable_sms_reminder_et.xml b/app/src/main/res/drawable/bg_enable_sms_reminder_et.xml new file mode 100644 index 0000000000..7bc2fe1a61 --- /dev/null +++ b/app/src/main/res/drawable/bg_enable_sms_reminder_et.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_enable_wechat_reminders.xml b/app/src/main/res/drawable/bg_enable_wechat_reminders.xml new file mode 100644 index 0000000000..487238ff1a --- /dev/null +++ b/app/src/main/res/drawable/bg_enable_wechat_reminders.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_reserve_only_wechat_unable_submit.xml b/app/src/main/res/drawable/bg_reserve_only_wechat_unable_submit.xml new file mode 100644 index 0000000000..1bf22d9efb --- /dev/null +++ b/app/src/main/res/drawable/bg_reserve_only_wechat_unable_submit.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_sms_reminders_enable.xml b/app/src/main/res/drawable/bg_sms_reminders_enable.xml new file mode 100644 index 0000000000..6d0fc6b518 --- /dev/null +++ b/app/src/main/res/drawable/bg_sms_reminders_enable.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_sms_reminders_unable.xml b/app/src/main/res/drawable/bg_sms_reminders_unable.xml new file mode 100644 index 0000000000..d3a6306522 --- /dev/null +++ b/app/src/main/res/drawable/bg_sms_reminders_unable.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_download.xml b/app/src/main/res/drawable/ic_download.xml new file mode 100644 index 0000000000..e3927df971 --- /dev/null +++ b/app/src/main/res/drawable/ic_download.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_edit_bind_info.xml b/app/src/main/res/drawable/ic_edit_bind_info.xml new file mode 100644 index 0000000000..a3487ff3e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_edit_bind_info.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_reserve_only_wechat_recommend.xml b/app/src/main/res/drawable/ic_reserve_only_wechat_recommend.xml new file mode 100644 index 0000000000..9e913cd8b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_reserve_only_wechat_recommend.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_reserve_open_wechat_reminder.xml b/app/src/main/res/drawable/ic_reserve_open_wechat_reminder.xml new file mode 100644 index 0000000000..387abdaa04 --- /dev/null +++ b/app/src/main/res/drawable/ic_reserve_open_wechat_reminder.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_reserve_question.xml b/app/src/main/res/drawable/ic_reserve_question.xml new file mode 100644 index 0000000000..c63c4740bd --- /dev/null +++ b/app/src/main/res/drawable/ic_reserve_question.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_reserve_remind_recommend.xml b/app/src/main/res/drawable/ic_reserve_remind_recommend.xml new file mode 100644 index 0000000000..b3f41a693f --- /dev/null +++ b/app/src/main/res/drawable/ic_reserve_remind_recommend.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_reserve_reminder_close.xml b/app/src/main/res/drawable/ic_reserve_reminder_close.xml new file mode 100644 index 0000000000..fe94cbc4b2 --- /dev/null +++ b/app/src/main/res/drawable/ic_reserve_reminder_close.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/layout/activity_my_game.xml b/app/src/main/res/layout/activity_my_game.xml new file mode 100644 index 0000000000..d30e3532dd --- /dev/null +++ b/app/src/main/res/layout/activity_my_game.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_reserve.xml b/app/src/main/res/layout/dialog_reserve.xml index d686dc2e83..a9c1264899 100644 --- a/app/src/main/res/layout/dialog_reserve.xml +++ b/app/src/main/res/layout/dialog_reserve.xml @@ -1,40 +1,106 @@ - + + + + android:src="@drawable/bg_reserve_online_reminder" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:layout_marginStart="24dp" + android:layout_marginTop="80dp" + android:textColor="@color/text_secondary" + android:textSize="13sp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="您预约的4款游戏上线了~" /> + android:paddingHorizontal="20dp" + android:clipToPadding="false" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_title" + tools:itemCount="1" + tools:listitem="@layout/dialog_reserve_item" /> + android:id="@+id/tv_auto_download_tips" + style="@style/TextCaption1" + android:layout_width="0dp" + android:layout_height="52dp" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="16dp" + android:background="@drawable/bg_shape_f8_radius_12" + android:drawablePadding="12dp" + android:ellipsize="end" + android:gravity="center_vertical" + android:maxLines="2" + android:paddingHorizontal="12dp" + android:textColor="@color/text_secondary" + app:drawableEndCompat="@drawable/ic_forum_arrow_right" + app:drawableStartCompat="@drawable/ic_download" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rv_games" + tools:text="@string/reserve_reminder_auto_download_with_name" /> - + + + + + + diff --git a/app/src/main/res/layout/dialog_reserve_item.xml b/app/src/main/res/layout/dialog_reserve_item.xml index fd6998d1a9..50f4db13ec 100644 --- a/app/src/main/res/layout/dialog_reserve_item.xml +++ b/app/src/main/res/layout/dialog_reserve_item.xml @@ -6,23 +6,25 @@ android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical" - android:paddingLeft="8dp" - android:paddingRight="8dp"> + android:paddingHorizontal="4dp"> - + fresco:roundedCornerRadius="12dp" /> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_reserve_success_with_sms.xml b/app/src/main/res/layout/dialog_reserve_success_with_sms.xml new file mode 100644 index 0000000000..fb988f9202 --- /dev/null +++ b/app/src/main/res/layout/dialog_reserve_success_with_sms.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wechat_binding_conflict.xml b/app/src/main/res/layout/dialog_wechat_binding_conflict.xml new file mode 100644 index 0000000000..df4fa264c8 --- /dev/null +++ b/app/src/main/res/layout/dialog_wechat_binding_conflict.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wechat_binding_failed.xml b/app/src/main/res/layout/dialog_wechat_binding_failed.xml new file mode 100644 index 0000000000..49608d9f46 --- /dev/null +++ b/app/src/main/res/layout/dialog_wechat_binding_failed.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_followed_game.xml b/app/src/main/res/layout/item_followed_game.xml index c125329f74..ba2bab5efc 100644 --- a/app/src/main/res/layout/item_followed_game.xml +++ b/app/src/main/res/layout/item_followed_game.xml @@ -1,5 +1,5 @@ - + layout="@layout/recycler_my_reserve" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:paddingBottom="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/game_item_included"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_my_reserve_game.xml b/app/src/main/res/layout/item_my_reserve_game.xml new file mode 100644 index 0000000000..ba2bab5efc --- /dev/null +++ b/app/src/main/res/layout/item_my_reserve_game.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_popup_reserve_reminder_option_item.xml b/app/src/main/res/layout/layout_popup_reserve_reminder_option_item.xml new file mode 100644 index 0000000000..b708dfcf19 --- /dev/null +++ b/app/src/main/res/layout/layout_popup_reserve_reminder_option_item.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_reserve_only_wechat_unable.xml b/app/src/main/res/layout/layout_reserve_only_wechat_unable.xml new file mode 100644 index 0000000000..fc003ca35b --- /dev/null +++ b/app/src/main/res/layout/layout_reserve_only_wechat_unable.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_reserve_sms_reminder_enable.xml b/app/src/main/res/layout/layout_reserve_sms_reminder_enable.xml new file mode 100644 index 0000000000..c374c98f2f --- /dev/null +++ b/app/src/main/res/layout/layout_reserve_sms_reminder_enable.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_reserve_sms_reminder_unable.xml b/app/src/main/res/layout/layout_reserve_sms_reminder_unable.xml new file mode 100644 index 0000000000..6b061fe9d2 --- /dev/null +++ b/app/src/main/res/layout/layout_reserve_sms_reminder_unable.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_reserve_wechat_reminder_enable.xml b/app/src/main/res/layout/layout_reserve_wechat_reminder_enable.xml new file mode 100644 index 0000000000..2d33d6f1de --- /dev/null +++ b/app/src/main/res/layout/layout_reserve_wechat_reminder_enable.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_reserve_wechat_reminder_unable.xml b/app/src/main/res/layout/layout_reserve_wechat_reminder_unable.xml new file mode 100644 index 0000000000..49ce80e7b4 --- /dev/null +++ b/app/src/main/res/layout/layout_reserve_wechat_reminder_unable.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pop_real_name_tips.xml b/app/src/main/res/layout/pop_real_name_tips.xml new file mode 100644 index 0000000000..5842d37c35 --- /dev/null +++ b/app/src/main/res/layout/pop_real_name_tips.xml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pop_reserve_all_select.xml b/app/src/main/res/layout/pop_reserve_all_select.xml new file mode 100644 index 0000000000..5fc58bcdf2 --- /dev/null +++ b/app/src/main/res/layout/pop_reserve_all_select.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_my_reserve.xml b/app/src/main/res/layout/recycler_my_reserve.xml new file mode 100644 index 0000000000..3ed802aa6a --- /dev/null +++ b/app/src/main/res/layout/recycler_my_reserve.xml @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_game_reserve.xml b/app/src/main/res/menu/menu_game_reserve.xml new file mode 100644 index 0000000000..dec1b382c5 --- /dev/null +++ b/app/src/main/res/menu/menu_game_reserve.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 3a97b5ae6b..672279679c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -593,5 +593,61 @@ 啟動遊戲 加載完成,馬上開始遊戲吧 %1$d款遊戲加載完成,馬上開始遊戲吧 + 遊戲上線後,您將在訊息中心收到通知 + 遊戲上線後,您將在訊息中心收到通知,及以下方式提醒 + 簡訊提醒 + 開啟後將收到免費簡訊提醒 + 開啟 + 微信提醒 + 只需一次設置,即可永久有效 + 遊戲上線後WiFi環境自動下載 + 根據相關政策要求,遊戲自動下載需通過實名認證,馬上實名 > + 修改手機號 + 修改手機號碼 + 驗證手機號 + 關閉簡訊提醒 + 建議開啟微信提醒,不錯過任何預約的遊戲,只需一次設置,即可永久有效 + 開啟微信提醒 + 開啟簡訊提醒 + 遊戲上線後,將透過免費簡訊提醒您;手機號僅用於預約提醒 + 手機號碼格式錯誤,請檢查並重新輸入 + 請輸入簡訊驗證碼 + 請輸入驗證碼 + 已經發送至%1$s + 更換綁定微信 + 綁定失敗 + 該微訊號(微信暱稱)已經綁定在另一個衝突號碼上,衝突號碼可能是你先前登入的 + 目前號 + 衝突號 + 遇到衝突如何換綁? + ID:%1$s + 遇到衝突如何換綁? + 1.請先退出目前號【我的光環--設定--退出帳號】\n2.再重新登入衝突號,在【我的光環-微信提醒】的「第一步:綁定微信」點選解除綁定\n3.微信解除綁定成功後,再退出衝突號,並重新登入目前號,進入【我的光環-微信提醒】進行綁定 + 用戶ID複製成功 + 重新發送 + 重新發送(%1$ss) + 您預約的%1$s款遊戲上線囉~ + 共%1$s款遊戲已經開始WiFi環境自動下載 + %1$s 共%2$s款遊戲已經開始WiFi環境自動下載 + 共%1$s款遊戲已加入下載隊列,連接WiFi後自動下載 + %1$s 共%2$s款遊戲已加入下載隊列,連接WiFi後自動下載 + 查看全部預約 + 已開啟自動下載 + 批次開啟自動下載 + 開啟WiFi自動下載 + 大量管理 + 取消WiFi自動下載 + 取消預約 + 全選 + (%1$s) + 簡訊驗證透過 + 簡訊驗證失敗,請重新輸入驗證碼 + 驗證已取消 + 遊戲已開啟WiFi環境自動下載 + 遊戲已取消WiFi環境自動下載” + 遊戲暫不支援WiFi環境自動下載 + 我的遊戲 + 玩過 + 預約 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3008e9ad73..e31c4f440a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -425,7 +425,7 @@ 完美,太棒了 将已安装游戏标记为玩过的游戏 - 你有%1$d]]>款预约的游戏上线啦 + 你预约的%1$d款游戏上线啦 欢迎您使用光环助手!\n在您使用光环助手之前,请您认真阅读《用户协议》和《隐私政策》的全部内容,以了解用户权利义务和个人信息处理规则。主要内容向您说明如下:\n1.为了正常地提供优质的产品服务,基于您的授权我们会获取必要的个人信息,您有权拒绝或取消授权\n2.我们会采取合理的安全措施保护您的个人信息,防止数据被不当使用或未经授权的情况下被访问、公开披露、使用、修改、损坏、丢失或泄漏。\n3.未经您同意,我们不会从第三方处获取、共享或向其提供您的信息\n4.您可以查询、更正、删除您的个人信息,我们也提供账户注销的渠道 此游戏下载资源由第三方提供。若该资源侵犯了您的合法权益或违反了当地法规,请点击页面右上角-版权申诉,按指引发起申诉,我们会尽快联系您并解决。 游戏停服更新维护中,为避免情绪化内容对游戏评分带来的影响,因此开启停服保护功能。在停服保护状态期间,所新增及修改发布的评分将不计入总分,所评分评论内容在展示上也会有文字提示告知其他玩家。\n\n感谢您的配合及谅解,祝您游戏愉快!\n\n光环助手会持续关注产品建议及反馈,如您在使用过程中有任何问题,欢迎向我们反馈。 @@ -593,4 +593,60 @@ 启动游戏 加载完成,马上开始游戏吧 %1$d款游戏加载完成,马上开始游戏吧 + 游戏上线后,您将在消息中心收到通知 + 游戏上线后,您将在消息中心收到通知,及以下方式提醒 + 短信提醒 + 开启后将收到免费短信提醒 + 开启 + 微信提醒 + 只需一次设置,即可永久有效 + 游戏上线后WiFi环境自动下载 + 根据相关政策要求,游戏自动下载需要通过实名认证,马上实名 > + 修改手机号 + 修改手机号码 + 验证手机号 + 关闭短信提醒 + 建议开启微信提醒,不错过任何预约的游戏,只需一次设置,即可永久有效 + 开启微信提醒 + 开启短信提醒 + 游戏上线后,将通过免费短信提醒您;手机号仅用于预约提醒 + 手机号格式错误,请检查并重新输入 + 请输入短信验证码 + 请输入验证码 + 已经发送至%1$s + 更换绑定微信 + 绑定失败 + 该微信号(微信昵称)已经绑定在另外一个冲突号上,冲突号可能是你之前登录的 + 当前号 + 冲突号 + 遇到冲突如何换绑? + ID:%1$s + 遇到冲突如何换绑? + 1.请先退出当前号【我的光环--设置--退出账号】\n2.再重新登录冲突号,在【我的光环-微信提醒】的“第一步:绑定微信”点击解除绑定\n3.微信解除绑定成功后,再退出冲突号,并重新登录当前号,进入【我的光环-微信提醒】进行绑定 + 用户ID复制成功 + 重新发送 + 重新发送(%1$ss) + 您预约的%1$s款游戏上线啦~ + 共%1$s款游戏已经开始WiFi环境自动下载 + %1$s 共%2$s款游戏已经开始WiFi环境自动下载 + 共%1$s款游戏已加入下载队列,连接WiFi后自动下载 + %1$s 共%2$s款游戏已加入下载队列,连接WiFi后自动下载 + 查看全部预约 + 已开启自动下载 + 批量开启自动下载 + 开启WiFi自动下载 + 批量管理 + 取消WiFi自动下载 + 取消预约 + 全选 + (%1$s) + 短信验证通过 + 短信验证失败,请重新输入验证码 + 验证已取消 + 游戏已开启WiFi环境自动下载 + 游戏已取消WiFi环境自动下载” + 游戏暂不支持WiFi环境自动下载 + 我的游戏 + 玩过 + 预约 diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java index 0cb9ab63c2..ec471933a6 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java @@ -410,6 +410,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus showDialog.setUsed(true); IPackageInstallerProvider packageInstallerConfig = (IPackageInstallerProvider) ARouter.getInstance().build(RouteConsts.provider.packageInstaller).navigation(); if (DOWNLOAD_HIJACK.equals(showDialog.getType())) { + DownloadEntity downloadEntity = showDialog.getDownloadEntity(); DialogHelper.showQqSessionDialog(this);// 建议用户联系客服 } else if (PLUGGABLE.equals(showDialog.getType())) { DownloadEntity downloadEntity = showDialog.getDownloadEntity(); diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java index e68bc741d2..2193fab82f 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java @@ -469,4 +469,6 @@ public class Constants { public static final String SP_BRAND_NEW_FIRST_LAUNCH_TIME = "brand_new_first_launch_time"; // 全新安装用户首次启动时间 + + public static final String IS_RESERVE_ONLINE_REMINDER = "is_reserve_online_reminder"; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/ErrorEntity.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/ErrorEntity.kt index f225b1ec0a..18eddfbf4f 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/entity/ErrorEntity.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/ErrorEntity.kt @@ -1,6 +1,8 @@ package com.gh.gamecenter.common.entity +import android.os.Parcelable import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize class ErrorEntity( var code: Int? = 0, @@ -32,7 +34,11 @@ class ErrorEntity( val link: String = "", val text: String = "", @SerializedName("link_community", alternate = ["community"]) - var community: CommunityEntity? = CommunityEntity() + var community: CommunityEntity? = CommunityEntity(), + @SerializedName("bind") + private val _bind: Boolean? = null, + @SerializedName("user_conflict") + private val _userConflict: UserConflict? = null ) { // 问题关注数默认是1 fun getFollowCount(): Int { @@ -49,5 +55,33 @@ class ErrorEntity( community = community ) } + + val userConflict: UserConflict + get() = _userConflict ?: UserConflict() + + @Parcelize + data class UserConflict( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("name") + private val _name: String? = null, + @SerializedName("icon") + private val _icon: String? = null, + @SerializedName("_seq") + private val _seq: String? = null + ) : Parcelable { + + val id: String + get() = _id ?: "" + + val name: String + get() = _name ?: "" + + val icon: String + get() = _icon ?: "" + + val seq: String + get() = _seq ?: "" + } } } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/WechatConfigEntity.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/WechatConfigEntity.kt index f7375f6381..e183725240 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/entity/WechatConfigEntity.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/WechatConfigEntity.kt @@ -1,11 +1,18 @@ package com.gh.gamecenter.common.entity +import android.os.Parcelable import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize +@Parcelize class WechatConfigEntity( var bind: Boolean = false,//用户是否绑定微信 var follow: Boolean = false,//绑定的微信是否关注光环助手 var notice: Boolean = false,//是否打开微信通知开关 @SerializedName("nickname") var nickName: String = "", -) \ No newline at end of file +) : Parcelable { + + val isReminderEnable: Boolean + get() = bind && follow && notice +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt index 4beb3cd304..d6d26e8f71 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt @@ -121,6 +121,9 @@ object SensorsBridge { private const val KEY_CONTENT_ID = "content_id" private const val KEY_CONTENT_NAME = "content_name" private const val KEY_PROFILE = "profile" + private const val KEY_WECHAT_REMIND = "wechat_remind" + private const val KEY_MESSAGE_REMIND = "message_remind" + private const val KEY_AUTOMATIC_DOWNLOAD = "automatic_download" private const val EVENT_GAME_DETAIL_PAGE_TAB_SELECT = "GameDetailPageTabSelect" private const val EVENT_GAME_DETAIL_PAGE_TAG_CLICK = "GameDetailPageGameTagClick" @@ -295,6 +298,15 @@ object SensorsBridge { private const val EVENT_BYPASS_BROWSING = "BypassBrowsing" private const val EVENT_FAIL_BYPASS = "FailBypass" + private const val EVENT_CANCEL_APPOINTMENT_DIALOG_SHOW = "CancelAppointmentDialogShow" + private const val EVENT_CANCEL_APPOINTMENT_DIALOG_CLICK = "CancelAppointmentDialogClick" + private const val EVENT_APPOINTMENT_SUCCESS_DIALOG_SHOW = "AppointmentSuccessDialogShow" + private const val EVENT_APPOINTMENT_SUCCESS_DIALOG_CLICK = "AppointmentSuccessDialogClick" + private const val EVENT_OPEN_APPOINTMENT_AUTOMATIC_DOWNLOAD = "OpenAppointmentAutomaticDownload" + private const val EVENT_CANCEL_APPOINTMENT_AUTOMATIC_DOWNLOAD = "CancelAppointmentAutomaticDownload" + private const val EVENT_APPOINTMENT_GAME_ONLINE_DIALOG_SHOW = "AppointmentGameOnlineDialogShow" + private const val EVENT_APPOINTMENT_GAME_ONLINE_DIALOG_CLICK = "AppointmentGameOnlineDialogClick" + private var mIsSensorsEnabled = false private val mSensor by lazy { @@ -4050,6 +4062,7 @@ object SensorsBridge { } trackEvent(EVENT_DOWNLOAD_SUSPENDED_WINDOW_RESULT_RETURN, json) } + /** * 事件ID:InstallationPromptBarShow * 事件名称:安装提示条展示事件 @@ -4818,4 +4831,166 @@ object SensorsBridge { } trackEvent(EVENT_GAME_COLLECT_SEARCH_RESULT_CLICK, json) } + /** + * 事件ID:CancelAppointmentDialogShow + * 事件名称:游戏取消预约弹窗展示事件 + * 触发时机:触发游戏取消弹窗时上报 + */ + fun trackCancelAppointmentDialogShow( + gameId: String, + gameName: String, + gameType: String + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + } + trackEvent(EVENT_CANCEL_APPOINTMENT_DIALOG_SHOW, json) + } + + /** + * 事件ID:CancelAppointmentDialogClick + * 事件名称:游戏取消预约弹窗点击事件 + * 触发时机:点击游戏取消弹窗时上报 + */ + fun trackCancelAppointmentDialogClick( + gameId: String, + gameName: String, + gameType: String, + buttonName: String + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + KEY_BUTTON_NAME to buttonName + } + trackEvent(EVENT_CANCEL_APPOINTMENT_DIALOG_CLICK, json) + } + + /** + * 事件ID:AppointmentSuccessDialogShow + * 事件名称:预约成功弹窗展示事件 + * 触发时机:触发游戏预约成功提示弹窗时上报 + */ + fun trackAppointmentSuccessDialogShow( + gameId: String, + gameName: String, + gameType: String, + wechatRemind: Boolean, + messageRemind: Boolean?, + automaticDownload: Boolean? + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + KEY_WECHAT_REMIND to wechatRemind + KEY_MESSAGE_REMIND to messageRemind + KEY_AUTOMATIC_DOWNLOAD to automaticDownload + } + trackEvent(EVENT_APPOINTMENT_SUCCESS_DIALOG_SHOW, json) + } + + /** + * 事件ID:AppointmentSuccessDialogClick + * 事件名称:预约成功弹窗点击事件 + * 触发时机:游戏预约成功提示弹窗点击时上报 + */ + fun trackAppointmentSuccessDialogClick( + gameId: String, + gameName: String, + gameType: String, + buttonName: String, + wechatRemind: Boolean? = null, + messageRemind: Boolean? = null, + automaticDownload: Boolean? = null + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + KEY_BUTTON_NAME to buttonName + KEY_WECHAT_REMIND to wechatRemind + KEY_MESSAGE_REMIND to messageRemind + KEY_AUTOMATIC_DOWNLOAD to automaticDownload + } + trackEvent(EVENT_APPOINTMENT_SUCCESS_DIALOG_CLICK, json) + } + + /** + * 事件ID:OpenAppointmentAutomaticDownload + * 事件名称:预约开启自动下载事件 + * 触发时机:我的游戏-预约tab 开启自动下载时上报 + */ + fun trackOpenAppointmentAutomaticDownload( + gameId: String, + gameName: String, + gameType: String + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + } + trackEvent(EVENT_OPEN_APPOINTMENT_AUTOMATIC_DOWNLOAD, json) + } + + /** + * 事件ID:CancelAppointmentAutomaticDownload + * 事件名称:预约取消自动下载事件 + * 触发时机:我的游戏-预约tab 取消自动下载时上报 + */ + fun trackCancelAppointmentAutomaticDownload( + gameId: String, + gameName: String, + gameType: String + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + } + trackEvent(EVENT_CANCEL_APPOINTMENT_AUTOMATIC_DOWNLOAD, json) + } + + /** + * 事件ID:AppointmentGameOnlineDialogShow + * 事件名称:预约游戏上线弹窗展示事件 + * 触发时机:触发预约游戏上线提示弹窗时上报 + */ + fun trackAppointmentGameOnlineDialogShow( + gameId: String, + gameName: String, + gameType: String + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + } + trackEvent(EVENT_APPOINTMENT_GAME_ONLINE_DIALOG_SHOW, json) + + } + + /** + * 事件ID:AppointmentGameOnlineDialogClick + * 事件名称:预约游戏上线弹窗点击事件 + * 触发时机:触发预约游戏上线提示弹窗点击时上报 + */ + fun trackAppointmentGameOnlineDialogClick( + buttonName: String, + gameId: String? = null, + gameName: String? = null, + gameType: String? = null, + ) { + val json = json { + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_GAME_TYPE to gameType + KEY_BUTTON_NAME to buttonName + } + trackEvent(EVENT_APPOINTMENT_GAME_ONLINE_DIALOG_CLICK, json) + } } \ No newline at end of file diff --git a/module_common/src/main/res/drawable-night/ic_selector_default.xml b/module_common/src/main/res/drawable-night/ic_selector_default.xml index b7a2ebe93e..3f457e45c3 100644 --- a/module_common/src/main/res/drawable-night/ic_selector_default.xml +++ b/module_common/src/main/res/drawable-night/ic_selector_default.xml @@ -7,5 +7,5 @@ android:pathData="M8,8m-7.25,0a7.25,7.25 0,1 1,14.5 0a7.25,7.25 0,1 1,-14.5 0" android:strokeWidth="1.5" android:fillColor="#00000000" - android:strokeColor="#EEEEEE" /> + android:strokeColor="@color/ui_divider_2" /> diff --git a/module_common/src/main/res/drawable/background_shape_white_radius_12.xml b/module_common/src/main/res/drawable/background_shape_white_radius_12.xml new file mode 100644 index 0000000000..1eb73f0dc1 --- /dev/null +++ b/module_common/src/main/res/drawable/background_shape_white_radius_12.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/drawable/background_shape_white_radius_8.xml b/module_common/src/main/res/drawable/background_shape_white_radius_8.xml index 78e438bd2c..72d6f3f8be 100644 --- a/module_common/src/main/res/drawable/background_shape_white_radius_8.xml +++ b/module_common/src/main/res/drawable/background_shape_white_radius_8.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/module_common/src/main/res/drawable/bg_shape_ui_container_2_radius_2.xml b/module_common/src/main/res/drawable/bg_shape_ui_container_2_radius_2.xml new file mode 100644 index 0000000000..bf32a41d73 --- /dev/null +++ b/module_common/src/main/res/drawable/bg_shape_ui_container_2_radius_2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/drawable/ic_explain.xml b/module_common/src/main/res/drawable/ic_explain.xml new file mode 100644 index 0000000000..5d75fdbfe4 --- /dev/null +++ b/module_common/src/main/res/drawable/ic_explain.xml @@ -0,0 +1,10 @@ + + + diff --git a/module_common/src/main/res/drawable/ic_selector_default.xml b/module_common/src/main/res/drawable/ic_selector_default.xml index b7a2ebe93e..3f457e45c3 100644 --- a/module_common/src/main/res/drawable/ic_selector_default.xml +++ b/module_common/src/main/res/drawable/ic_selector_default.xml @@ -7,5 +7,5 @@ android:pathData="M8,8m-7.25,0a7.25,7.25 0,1 1,14.5 0a7.25,7.25 0,1 1,-14.5 0" android:strokeWidth="1.5" android:fillColor="#00000000" - android:strokeColor="#EEEEEE" /> + android:strokeColor="@color/ui_divider_2" /> diff --git a/module_common/src/main/res/drawable/ic_selector_selected_unable.xml b/module_common/src/main/res/drawable/ic_selector_selected_unable.xml new file mode 100644 index 0000000000..40bb27ded2 --- /dev/null +++ b/module_common/src/main/res/drawable/ic_selector_selected_unable.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/module_common/src/main/res/drawable/ic_selector_unable.xml b/module_common/src/main/res/drawable/ic_selector_unable.xml new file mode 100644 index 0000000000..6b2ff669b8 --- /dev/null +++ b/module_common/src/main/res/drawable/ic_selector_unable.xml @@ -0,0 +1,16 @@ + + + + diff --git a/module_common/src/main/res/values-night/colors.xml b/module_common/src/main/res/values-night/colors.xml index 4ee02d3a44..ee3d2ef661 100644 --- a/module_common/src/main/res/values-night/colors.xml +++ b/module_common/src/main/res/values-night/colors.xml @@ -55,6 +55,8 @@ #0AFFFFFF #333333 + #1EFFFFFF + #33FFFFFF #0A2888E0 @@ -386,4 +388,6 @@ #14A3A3 #E5A82E #E58627 + + #0Dffffff \ No newline at end of file diff --git a/module_common/src/main/res/values/colors.xml b/module_common/src/main/res/values/colors.xml index 70c8925fe6..93eefee86f 100644 --- a/module_common/src/main/res/values/colors.xml +++ b/module_common/src/main/res/values/colors.xml @@ -58,6 +58,9 @@ #EEEEEE #333333 + #12000000 + #1A000000 + #0A2496FF @@ -437,4 +440,6 @@ #FF9933 #FF5447 #1AFF5447 + + #0D000000 \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatBindHelperProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatBindHelperProvider.kt index 7fbf565b4e..8872566f82 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatBindHelperProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatBindHelperProvider.kt @@ -2,6 +2,6 @@ package com.gh.gamecenter.core.provider import com.alibaba.android.arouter.facade.template.IProvider -interface IWechatBindHelperProvider : IProvider { - fun getWechatConfig(callback: (() -> Unit)? = null) +interface IWechatBindHelperProvider : IProvider { + fun getWechatConfig(callback: ((T) -> Unit)? = null) } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt index 94625b8ea4..d8246f7ad3 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt @@ -320,6 +320,8 @@ data class GameEntity( // 资讯数据 @SerializedName("article_data") var articleData: CommunityTopEntity? = null, + @SerializedName("wifi_auto_download") + private val _wifiAutoDownload: Boolean? = null, // 只有预约上线弹窗/我的预约接口接口会返回这个字段 // 本地字段 // 用来标记在专题中的序号,仅用于曝光记录 @@ -651,6 +653,14 @@ data class GameEntity( val abbreviation: String get() = _abbreviation ?: "" + @IgnoredOnParcel + val wifiAutoDownload + get() = _wifiAutoDownload ?: false + + // 游戏是否有 wifi自动下载功能 + val wifiAutoDownloadEnable: Boolean + get() = _wifiAutoDownload != null + //是否需要弹出试玩弹窗 fun isShowVersionNumber(): Boolean { return versionNumber == "无版号无内购有弹窗" diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/ReserveOnlineEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/ReserveOnlineEntity.kt new file mode 100644 index 0000000000..663ed72817 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/ReserveOnlineEntity.kt @@ -0,0 +1,25 @@ +package com.gh.gamecenter.feature.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +data class ReserveOnlineEntity( + @SerializedName("games") + private val _games: List? = null, + @SerializedName("wifi_auto_download_total") + private val _wifiAutoDownloadTotal: Int? = null, + @SerializedName("games_total") + private val _gamesTotal: Int? = null +) : Parcelable { + + val games: List + get() = _games ?: emptyList() + + val wifiAutoDownloadTotal: Int + get() = _wifiAutoDownloadTotal ?: 0 + + val gamesTotal: Int + get() = _gamesTotal ?: 0 +} \ No newline at end of file diff --git a/module_core_feature/src/main/res/drawable/bg_auto_download.xml b/module_core_feature/src/main/res/drawable/bg_auto_download.xml new file mode 100644 index 0000000000..6535a825c1 --- /dev/null +++ b/module_core_feature/src/main/res/drawable/bg_auto_download.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/module_core_feature/src/main/res/drawable/ic_reserve_more.xml b/module_core_feature/src/main/res/drawable/ic_reserve_more.xml new file mode 100644 index 0000000000..7de17b0866 --- /dev/null +++ b/module_core_feature/src/main/res/drawable/ic_reserve_more.xml @@ -0,0 +1,10 @@ + + + diff --git a/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt b/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt index e473e45fa5..efc90e39ae 100644 --- a/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt +++ b/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt @@ -1,7 +1,6 @@ package com.gh.gamecenter.setting.view import android.annotation.SuppressLint -import android.app.AlertDialog import android.app.Application import android.app.Dialog import android.content.Intent @@ -9,13 +8,9 @@ import android.os.Build import android.os.Bundle import android.provider.Settings import android.text.TextUtils -import android.view.Gravity import android.view.View -import android.widget.EditText -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.content.ContextCompat import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModelProvider import com.alibaba.android.arouter.launcher.ARouter @@ -23,18 +18,14 @@ import com.blankj.utilcode.util.LanguageUtils import com.gh.gamecenter.common.BuildConfig import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.base.fragment.ToolbarFragment -import com.gh.gamecenter.common.constant.Config import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.entity.WechatConfigEntity -import com.gh.gamecenter.common.eventbus.EBReuse import com.gh.gamecenter.common.retrofit.ApiResponse import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.provider.* import com.gh.gamecenter.core.runOnIoThread -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.SPUtils @@ -54,7 +45,6 @@ import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import org.greenrobot.eventbus.EventBus import java.io.File import java.util.* @@ -130,13 +120,9 @@ class SettingsFragment : ToolbarFragment() { mUserViewModel.loginObsUserinfo.observe(this) { response: ApiResponse? -> if (response != null && response.data != null) { - val wechatBindHelper = ARouter.getInstance().build(RouteConsts.provider.wechatHelper) - .navigation() as? IWechatBindHelperProvider - wechatBindHelper?.getWechatConfig { - initWechatBindStatus() - } + mViewModel.getWechatConfig() } else { - initWechatBindStatus() + mViewModel.updateWechatConfig(null) } } @@ -145,10 +131,16 @@ class SettingsFragment : ToolbarFragment() { initUsageStats() } - private fun initWechatBindStatus() { - if (UserManager.getInstance().isLoggedIn) { - val json = SPUtils.getString(Constants.SP_WECHAT_CONFIG) - val notice = GsonUtils.fromJson(json, WechatConfigEntity::class.java)?.notice + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mViewModel.wechatConfig.observe(viewLifecycleOwner) { + initWechatBindStatus(it) + } + } + + private fun initWechatBindStatus(wechatEntity: WechatConfigEntity?) { + if (wechatEntity != null) { + val notice = wechatEntity.notice mBinding.wechatRemindItem.textMoreTv.text = if (notice == true) getString(com.gh.gamecenter.core.R.string.opened) else getString(R.string.closed) } else { @@ -579,6 +571,18 @@ class SettingsFragment : ToolbarFragment() { return size } + private val _wechatConfig = MutableLiveData() + val wechatConfig: LiveData = _wechatConfig + fun updateWechatConfig(wechatEntity: WechatConfigEntity?) { + _wechatConfig.value = wechatEntity + } + + fun getWechatConfig() { + val wechatBindHelper = ARouter.getInstance().build(RouteConsts.provider.wechatHelper) + .navigation() as? IWechatBindHelperProvider + wechatBindHelper?.getWechatConfig(::updateWechatConfig) + } + } override fun onDarkModeChanged() {