LockedController.kt 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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.Build
  28. import android.os.Bundle
  29. import android.os.Handler
  30. import android.os.Looper
  31. import android.util.Log
  32. import android.view.View
  33. import androidx.annotation.RequiresApi
  34. import androidx.biometric.BiometricPrompt
  35. import androidx.biometric.BiometricPrompt.PromptInfo
  36. import androidx.core.content.res.ResourcesCompat
  37. import androidx.fragment.app.FragmentActivity
  38. import autodagger.AutoInjector
  39. import com.nextcloud.talk.R
  40. import com.nextcloud.talk.application.NextcloudTalkApplication
  41. import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
  42. import com.nextcloud.talk.controllers.base.NewBaseController
  43. import com.nextcloud.talk.controllers.util.viewBinding
  44. import com.nextcloud.talk.databinding.ControllerLockedBinding
  45. import com.nextcloud.talk.utils.DisplayUtils
  46. import com.nextcloud.talk.utils.SecurityUtils
  47. import java.util.concurrent.Executor
  48. import java.util.concurrent.Executors
  49. @AutoInjector(NextcloudTalkApplication::class)
  50. class LockedController : NewBaseController(R.layout.controller_locked) {
  51. private val binding: ControllerLockedBinding by viewBinding(ControllerLockedBinding::bind)
  52. override val appBarLayoutType: AppBarLayoutType
  53. get() = AppBarLayoutType.EMPTY
  54. companion object {
  55. const val TAG = "LockedController"
  56. private const val REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112
  57. }
  58. override fun onViewBound(view: View) {
  59. super.onViewBound(view)
  60. sharedApplication!!.componentApplication.inject(this)
  61. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  62. binding.unlockContainer.setOnClickListener {
  63. unlock()
  64. }
  65. }
  66. }
  67. @RequiresApi(api = Build.VERSION_CODES.M)
  68. override fun onAttach(view: View) {
  69. super.onAttach(view)
  70. if (activity != null && resources != null) {
  71. DisplayUtils.applyColorToStatusBar(
  72. activity,
  73. ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null)
  74. )
  75. DisplayUtils.applyColorToNavigationBar(
  76. activity!!.window,
  77. ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null)
  78. )
  79. }
  80. checkIfWeAreSecure()
  81. }
  82. @RequiresApi(api = Build.VERSION_CODES.M)
  83. fun unlock() {
  84. checkIfWeAreSecure()
  85. }
  86. @RequiresApi(api = Build.VERSION_CODES.M)
  87. private fun showBiometricDialog() {
  88. val context: Context? = activity
  89. if (context != null) {
  90. val promptInfo = PromptInfo.Builder()
  91. .setTitle(
  92. String.format(
  93. context.getString(R.string.nc_biometric_unlock),
  94. context.getString(R.string.nc_app_name)
  95. )
  96. )
  97. .setNegativeButtonText(context.getString(R.string.nc_cancel))
  98. .build()
  99. val executor: Executor = Executors.newSingleThreadExecutor()
  100. val biometricPrompt = BiometricPrompt(
  101. (context as FragmentActivity?)!!, executor,
  102. object : BiometricPrompt.AuthenticationCallback() {
  103. override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
  104. super.onAuthenticationSucceeded(result)
  105. Log.d(TAG, "Fingerprint recognised successfully")
  106. Handler(Looper.getMainLooper()).post { router.popCurrentController() }
  107. }
  108. override fun onAuthenticationFailed() {
  109. super.onAuthenticationFailed()
  110. Log.d(TAG, "Fingerprint not recognised")
  111. }
  112. override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
  113. super.onAuthenticationError(errorCode, errString)
  114. showAuthenticationScreen()
  115. }
  116. }
  117. )
  118. val cryptoObject = SecurityUtils.getCryptoObject()
  119. if (cryptoObject != null) {
  120. biometricPrompt.authenticate(promptInfo, cryptoObject)
  121. } else {
  122. biometricPrompt.authenticate(promptInfo)
  123. }
  124. }
  125. }
  126. @RequiresApi(api = Build.VERSION_CODES.M)
  127. private fun checkIfWeAreSecure() {
  128. val keyguardManager = activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
  129. if (keyguardManager?.isKeyguardSecure == true && appPreferences!!.isScreenLocked) {
  130. if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences!!.screenLockTimeout)) {
  131. showBiometricDialog()
  132. } else {
  133. router.popCurrentController()
  134. }
  135. }
  136. }
  137. private fun showAuthenticationScreen() {
  138. val keyguardManager = activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
  139. val intent = keyguardManager?.createConfirmDeviceCredentialIntent(null, null)
  140. if (intent != null) {
  141. startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS)
  142. }
  143. }
  144. override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  145. super.onActivityResult(requestCode, resultCode, data)
  146. if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
  147. if (resultCode == Activity.RESULT_OK) {
  148. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  149. if (SecurityUtils.checkIfWeAreAuthenticated(appPreferences!!.screenLockTimeout)) {
  150. Log.d(TAG, "All went well, dismiss locked controller")
  151. router.popCurrentController()
  152. }
  153. }
  154. } else {
  155. Log.d(TAG, "Authorization failed")
  156. }
  157. }
  158. }
  159. }