限制一个应用程序一段时间 Kotlin

Restrict an application for some time Kotlin

我正在实现一个功能,当用户输入错误代码超过 5 次时,该功能会限制应用程序 10 分钟。我已经实现了在重新启动应用程序时不初始化计时器。当他们重新启动应用程序时,他们无法单击任何按钮,但可以看到一个持续 10 分钟的对话框。知道要使用哪个功能或者简单地解释一下我就可以了。 非常感谢所有帮助。

I have tried

我将开始时间存储在 sharedPreference 中并从 System.currentTimeMillis 中减去它。因此,当结果超过 600000(10 分钟)时,他们终于可以开始使用该应用程序了。

My question

  1. 我想知道如何在我的代码中重新启动应用程序时继续显示对话框。

My fragment

private lateinit var binding : FragmentHelpBinding
private lateinit var viewModel : HelpViewModel
private lateinit var mContext: MainActivity

override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
 ): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_help, container, false)

return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(HelpViewModel::class.java)
binding.viewModel = viewModel

otpTimeLimit()
}

private fun otpTimeLimit() = runBlocking {

val currentTime = System.currentTimeMillis()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var startTime = currentTime
var pref: SharedPreferences? = requireContext().getSharedPreferences("otpTrialLimit", MODE_PRIVATE)
pref?.edit()?.putLong("startTime", startTime)
pref?.edit()?.commit()
          
if (System.currentTimeMillis() - startTime <600000) {
    // dialog will show up here and should last for 10 min 
    clickDialog()
}else if (System.currentTimeMillis() - startTime >=600000) {
    // and when 10 minutes passed, it should dismiss              
}
}


private fun clickDialog() {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.help_dialog, null)
var alertDialog = AlertDialog.Builder(requireContext(), R.style.CustomAlertDialog)
    .setView(view)
    .create()
val titleText : TextView = view.findViewById(R.id.title_text_dialog)
val contentText : TextView = view.findViewById(R.id.content_text_dialog)

binding.btnGoSearch.setOnClickListener {
    titleText.text = getString(R.string.help_notification)
    contentText.text = getString(R.string.notification_content)
    alertDialog.show()
  }
}

这是实现此目的的一种策略。我假设您希望此错误状态持续存在,即使用户关闭并重新打开应用程序也是如此,因此计数和时间都需要持续存在。

此处的策略是处理视图模型中的大部分逻辑,以跟踪状态并将其持久化。在Fragment中,您只需要观察实时数据:在锁定状态下如果需要显示对话框,并且在状态转换为未锁定时如果对话框打开则隐藏对话框。

视图模型:

class HelpViewModel(application: Application): AndroidViewModel(application) {
    companion object {
        private const val SHARED_PREFS_NAME = "HelpViewModelPrefs"
        private const val KEY_LOCKOUT_START_TIME = "lockoutStartTime"
        private const val KEY_ERROR_COUNT = "errorCount"
        private const val LOCKOUT_DURATION_MILLIS = 60_000L
    }

    private val sharedPrefs = application.applicationContext.getSharedPreferences(
        SHARED_PREFS_NAME, Context.MODE_PRIVATE
    )
    private val _isErrorState = MutableLiveData<Boolean>()
    val isErrorState: LiveData<Boolean> get() = _isErrorState

    // Set up this property so it automatically persists and is initialized
    // with any previously persisted value.
    private var errorCount: Int = sharedPreferences.getInt(KEY_ERROR_COUNT, 0)
        set(value) {
            field = value
            sharedPreferences.edit {
                putInt(KEY_ERROR_COUNT, value)
            }
        }

    init {
        // Figure out if we are already locked out and set state appropriately.
        // If we are locked out, schedule when to end locked out state.

        val lockoutStartTime = sharedPrefs.getLong(KEY_LOCKOUT_START_TIME, 0)
        val lockoutEndTime = lockoutStartTime + LOCKOUT_DURATION_MILLIS
        val now = System.currentTimeMillis()
        if (now < lockoutEndTime) {
            isErrorState.value = true
            viewModelScope.launch {
                delay(lockoutEndTime - now)
                isErrorState.value = false
            }
        } else {
            isErrorState.value = false
        }
    }

    // call when the user performs an error
    fun incrementErrorCount() {
        errorCount++
        if (errorCount >= 5) {
            // Reset count, initiate lockout state, and schedule end of state
            errorCount = 0
            sharedPrefs.edit {
                putLong(KEY_LOCKOUT_START_TIME, System.currentTimeMillis())
            }
            isErrorState.value = true
            viewModelScope.launch {
                delay(LOCKOUT_DURATION_MILLIS)
                isErrorState.value = false
            }
        }
    }
}

在片段中,我们通过观察实时数据来处理状态。我们保留对任何当前打开的对话框的引用,因此如果状态在打开时转换,我们可以自动关闭它。我们在 Fragment 被销毁时清空引用,这样我们就不会泄漏它们。

private var lockoutDialog: AlertDialog? = null
private val viewModel: HelpViewModel by viewModels()
private var shouldShowLockoutDialog = false

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // ...

    viewModel.isErrorState.observe(viewLifecycleOwner) { isErrorState ->
        shouldShowLockoutDialog = isErrorState
        if (!isErrorState) {
            lockoutDialog?.dismiss()
        }
    }

    binding.btnGoSearch.setOnClickListener {
        if (shouldShowLockoutDialog) {
            showLockoutDialog()
        } else {
            // TODO some behavior when not locked out
        }
    }
}

private fun showLockoutDialog() {
    val layoutInflater = LayoutInflater.from(requireContext())
    val view = layoutInflater.inflate(R.layout.help_dialog, null).apply {
        findViewById<TextView>(R.id.title_text_dialog).text = 
            requireContext.getString(R.string.help_notification)
        findViewById<TextView>(R.id.content_text_dialog).text = 
            requireContext.getString(R.string.notification_content)
    }
    lockoutDialog = AlertDialog.Builder(
        requireContext(), 
        R.style.CustomAlertDialog
    )
        .setView(view)
        .setOnDismissListener { lockoutDialog = null }
        .create()
        .also { it.show() }
}