LockedController.kt 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * Nextcloud Talk application
  3. *
  4. * @author Mario Danic
  5. * @author Andy Scherzinger
  6. * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
  7. * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. package com.nextcloud.talk.controllers
  23. import android.app.Activity
  24. import android.app.KeyguardManager
  25. import android.content.Context
  26. import android.content.Intent
  27. import android.os.Handler
  28. import android.os.Looper
  29. import android.util.Log
  30. import android.view.View
  31. import androidx.biometric.BiometricPrompt
  32. import androidx.biometric.BiometricPrompt.PromptInfo
  33. import androidx.core.content.res.ResourcesCompat
  34. import androidx.fragment.app.FragmentActivity
  35. import autodagger.AutoInjector
  36. import com.nextcloud.talk.R
  37. import com.nextcloud.talk.application.NextcloudTalkApplication
  38. import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
  39. import com.nextcloud.talk.controllers.base.BaseController
  40. import com.nextcloud.talk.controllers.util.viewBinding
  41. import com.nextcloud.talk.databinding.ControllerLockedBinding
  42. import com.nextcloud.talk.utils.DisplayUtils
  43. import com.nextcloud.talk.utils.SecurityUtils
  44. import java.util.concurrent.Executor
  45. import java.util.concurrent.Executors
  46. @AutoInjector(NextcloudTalkApplication::class)
  47. class LockedController : BaseController(R.layout.controller_locked) {
  48. private val binding: ControllerLockedBinding? by viewBinding(ControllerLockedBinding::bind)
  49. override val appBarLayoutType: AppBarLayoutType
  50. get() = AppBarLayoutType.EMPTY
  51. companion object {
  52. const val TAG = "LockedController"
  53. private const val REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112
  54. }
  55. override fun onViewBound(view: View) {
  56. super.onViewBound(view)
  57. sharedApplication!!.componentApplication.inject(this)
  58. binding?.unlockContainer?.setOnClickListener {
  59. unlock()
  60. }
  61. }
  62. override fun onAttach(view: View) {
  63. super.onAttach(view)
  64. Log.d(TAG, "onAttach")
  65. if (activity != null && resources != null) {
  66. DisplayUtils.applyColorToStatusBar(
  67. activity,
  68. ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null)
  69. )
  70. DisplayUtils.applyColorToNavigationBar(
  71. activity!!.window,
  72. ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null)
  73. )
  74. }
  75. checkIfWeAreSecure()
  76. }
  77. override fun onDetach(view: View) {
  78. super.onDetach(view)
  79. Log.d(TAG, "onDetach")
  80. }
  81. fun unlock() {
  82. checkIfWeAreSecure()
  83. }
  84. private fun showBiometricDialog() {
  85. val context: Context? = activity
  86. if (context != null) {
  87. val promptInfo = PromptInfo.Builder()
  88. .setTitle(
  89. String.format(
  90. context.getString(R.string.nc_biometric_unlock),
  91. context.getString(R.string.nc_app_product_name)
  92. )
  93. )
  94. .setNegativeButtonText(context.getString(R.string.nc_cancel))
  95. .build()
  96. val executor: Executor = Executors.newSingleThreadExecutor()
  97. val biometricPrompt = BiometricPrompt(
  98. (context as FragmentActivity?)!!, executor,
  99. object : BiometricPrompt.AuthenticationCallback() {
  100. override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
  101. super.onAuthenticationSucceeded(result)
  102. Log.d(TAG, "Fingerprint recognised successfully")
  103. Handler(Looper.getMainLooper()).post { router.popCurrentController() }
  104. }
  105. override fun onAuthenticationFailed() {
  106. super.onAuthenticationFailed()
  107. Log.d(TAG, "Fingerprint not recognised")
  108. }
  109. override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
  110. super.onAuthenticationError(errorCode, errString)
  111. showAuthenticationScreen()
  112. }
  113. }
  114. )
  115. val cryptoObject = SecurityUtils.getCryptoObject()
  116. if (cryptoObject != null) {
  117. biometricPrompt.authenticate(promptInfo, cryptoObject)
  118. } else {
  119. biometricPrompt.authenticate(promptInfo)
  120. }
  121. }
  122. }
  123. private fun checkIfWeAreSecure() {
  124. val keyguardManager = activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
  125. if (keyguardManager?.isKeyguardSecure == true && appPreferences.isScreenLocked) {
  126. if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
  127. Log.d(TAG, "showBiometricDialog because 'we are NOT authenticated'...")
  128. showBiometricDialog()
  129. } else {
  130. Log.d(
  131. TAG,
  132. "popCurrentController because 'we are authenticated'. backstacksize= " +
  133. router.backstack.size
  134. )
  135. router.popCurrentController()
  136. }
  137. }
  138. }
  139. private fun showAuthenticationScreen() {
  140. Log.d(TAG, "showAuthenticationScreen")
  141. val keyguardManager = activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
  142. val intent = keyguardManager?.createConfirmDeviceCredentialIntent(null, null)
  143. if (intent != null) {
  144. startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS)
  145. }
  146. }
  147. override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  148. super.onActivityResult(requestCode, resultCode, data)
  149. if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
  150. if (resultCode == Activity.RESULT_OK) {
  151. if (
  152. SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)
  153. ) {
  154. Log.d(TAG, "All went well, dismiss locked controller")
  155. router.popCurrentController()
  156. }
  157. } else {
  158. Log.d(TAG, "Authorization failed")
  159. }
  160. }
  161. }
  162. }