Improve the parent key setup flow

This commit is contained in:
Jonas Lochmann 2022-05-09 02:00:00 +02:00
parent 9c887372f7
commit f7991e6dbf
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
9 changed files with 204 additions and 209 deletions

View file

@ -0,0 +1,35 @@
/*
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.extension
import android.widget.ViewFlipper
import io.timelimit.android.R
fun ViewFlipper.openNextWizardScreen(index: Int) {
if (displayedChild != index) {
setInAnimation(context, R.anim.wizard_open_step_in)
setOutAnimation(context, R.anim.wizard_open_step_out)
displayedChild = index
}
}
fun ViewFlipper.openPreviousWizardScreen(index: Int) {
if (displayedChild != index) {
setInAnimation(context, R.anim.wizard_close_step_in)
setOutAnimation(context, R.anim.wizard_close_step_out)
displayedChild = index
}
}

View file

@ -1,39 +0,0 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.login
import android.widget.Toast
import androidx.fragment.app.FragmentManager
import io.timelimit.android.R
import io.timelimit.android.extensions.showSafe
import io.timelimit.android.ui.manage.parent.key.ScanKeyDialogFragment
import io.timelimit.android.ui.manage.parent.key.ScannedKey
class CodeLoginDialogFragment: ScanKeyDialogFragment() {
companion object {
private const val DIALOG_TAG = "CodeLoginDialogFragment"
}
override fun handleResult(key: ScannedKey?) {
if (key == null) {
Toast.makeText(context!!, R.string.manage_user_key_invalid, Toast.LENGTH_SHORT).show()
} else {
(targetFragment as NewLoginFragment).tryCodeLogin(key)
}
}
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
}

View file

@ -15,6 +15,7 @@
*/ */
package io.timelimit.android.ui.login package io.timelimit.android.ui.login
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
@ -26,8 +27,8 @@ import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import io.timelimit.android.R import io.timelimit.android.R
@ -36,8 +37,12 @@ import io.timelimit.android.data.model.User
import io.timelimit.android.databinding.NewLoginFragmentBinding import io.timelimit.android.databinding.NewLoginFragmentBinding
import io.timelimit.android.extensions.setOnEnterListenr import io.timelimit.android.extensions.setOnEnterListenr
import io.timelimit.android.ui.MainActivity import io.timelimit.android.ui.MainActivity
import io.timelimit.android.ui.extension.openNextWizardScreen
import io.timelimit.android.ui.extension.openPreviousWizardScreen
import io.timelimit.android.ui.main.ActivityViewModelHolder import io.timelimit.android.ui.main.ActivityViewModelHolder
import io.timelimit.android.ui.main.getActivityViewModel import io.timelimit.android.ui.main.getActivityViewModel
import io.timelimit.android.ui.manage.parent.key.MissingBarcodeScannerDialogFragment
import io.timelimit.android.ui.manage.parent.key.ScanBarcode
import io.timelimit.android.ui.manage.parent.key.ScannedKey import io.timelimit.android.ui.manage.parent.key.ScannedKey
import io.timelimit.android.ui.view.KeyboardViewListener import io.timelimit.android.ui.view.KeyboardViewListener
@ -63,15 +68,23 @@ class NewLoginFragment: DialogFragment() {
private const val WAITING_FOR_SYNC = 8 private const val WAITING_FOR_SYNC = 8
} }
private val model: LoginDialogFragmentModel by lazy { private val model: LoginDialogFragmentModel by viewModels()
ViewModelProviders.of(this).get(LoginDialogFragmentModel::class.java)
}
private val inputMethodManager: InputMethodManager by lazy { private val inputMethodManager: InputMethodManager by lazy {
context!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
} }
private val activityModelHolder get() = requireActivity() as ActivityViewModelHolder private val activityModelHolder get() = requireActivity() as ActivityViewModelHolder
private val scanLoginCode = registerForActivityResult(ScanBarcode()) { barcode ->
barcode ?: return@registerForActivityResult
ScannedKey.tryDecode(barcode).let { key ->
if (key == null) Toast.makeText(requireContext(), R.string.manage_user_key_invalid, Toast.LENGTH_SHORT).show()
else tryCodeLogin(key)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -80,11 +93,11 @@ class NewLoginFragment: DialogFragment() {
} }
if (savedInstanceState == null) { if (savedInstanceState == null) {
model.tryDefaultLogin(getActivityViewModel(activity!!)) model.tryDefaultLogin(getActivityViewModel(requireActivity()))
} }
} }
override fun onCreateDialog(savedInstanceState: Bundle?) = object: BottomSheetDialog(context!!, theme) { override fun onCreateDialog(savedInstanceState: Bundle?) = object: BottomSheetDialog(requireContext(), theme) {
override fun onBackPressed() { override fun onBackPressed() {
if (!model.goBack()) { if (!model.goBack()) {
super.onBackPressed() super.onBackPressed()
@ -130,9 +143,11 @@ class NewLoginFragment: DialogFragment() {
} }
override fun onScanCodeRequested() { override fun onScanCodeRequested() {
CodeLoginDialogFragment().apply { try {
setTargetFragment(this@NewLoginFragment, 0) scanLoginCode.launch(null)
}.show(parentFragmentManager) } catch (ex: ActivityNotFoundException) {
MissingBarcodeScannerDialogFragment.newInstance().show(parentFragmentManager)
}
} }
} }
@ -158,7 +173,7 @@ class NewLoginFragment: DialogFragment() {
model.tryParentLogin( model.tryParentLogin(
password = password.text.toString(), password = password.text.toString(),
keepSignedIn = checkDontAskAgain.isChecked, keepSignedIn = checkDontAskAgain.isChecked,
model = getActivityViewModel(activity!!), model = getActivityViewModel(requireActivity()),
setAsDeviceUser = checkAssignMyself.isChecked setAsDeviceUser = checkAssignMyself.isChecked
) )
} }
@ -203,11 +218,7 @@ class NewLoginFragment: DialogFragment() {
dismissAllowingStateLoss() dismissAllowingStateLoss()
} }
is UserListLoginDialogStatus -> { is UserListLoginDialogStatus -> {
if (binding.switcher.displayedChild != USER_LIST) { binding.switcher.openPreviousWizardScreen(USER_LIST)
binding.switcher.setInAnimation(context!!, R.anim.wizard_close_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_close_step_out)
binding.switcher.displayedChild = USER_LIST
}
val users = status.usersToShow.map { LoginUserAdapterUser(it) } val users = status.usersToShow.map { LoginUserAdapterUser(it) }
@ -221,11 +232,7 @@ class NewLoginFragment: DialogFragment() {
null null
} }
is ParentUserLogin -> { is ParentUserLogin -> {
if (binding.switcher.displayedChild != PARENT_AUTH) { binding.switcher.openNextWizardScreen(PARENT_AUTH)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = PARENT_AUTH
}
binding.enterPassword.password.isEnabled = !status.isCheckingPassword binding.enterPassword.password.isEnabled = !status.isCheckingPassword
@ -255,7 +262,7 @@ class NewLoginFragment: DialogFragment() {
} }
if (status.wasPasswordWrong) { if (status.wasPasswordWrong) {
Toast.makeText(context!!, R.string.login_snackbar_wrong, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.login_snackbar_wrong, Toast.LENGTH_SHORT).show()
binding.enterPassword.password.setText("") binding.enterPassword.password.setText("")
model.resetPasswordWrong() model.resetPasswordWrong()
@ -264,40 +271,24 @@ class NewLoginFragment: DialogFragment() {
null null
} }
ParentUserLoginMissingTrustedTime -> { ParentUserLoginMissingTrustedTime -> {
if (binding.switcher.displayedChild != UNVERIFIED_TIME) { binding.switcher.openNextWizardScreen(UNVERIFIED_TIME)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = UNVERIFIED_TIME
}
null null
} }
is CanNotSignInChildHasNoPassword -> { is CanNotSignInChildHasNoPassword -> {
if (binding.switcher.displayedChild != CHILD_MISSING_PASSWORD) { binding.switcher.openNextWizardScreen(CHILD_MISSING_PASSWORD)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = CHILD_MISSING_PASSWORD
}
binding.childWithoutPassword.childName = status.childName binding.childWithoutPassword.childName = status.childName
null null
} }
is ChildAlreadyDeviceUser -> { is ChildAlreadyDeviceUser -> {
if (binding.switcher.displayedChild != CHILD_ALREADY_CURRENT_USER) { binding.switcher.openNextWizardScreen(CHILD_ALREADY_CURRENT_USER)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = CHILD_ALREADY_CURRENT_USER
}
null null
} }
is ChildUserLogin -> { is ChildUserLogin -> {
if (binding.switcher.displayedChild != CHILD_AUTH) { binding.switcher.openNextWizardScreen(CHILD_AUTH)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = CHILD_AUTH
}
binding.childPassword.password.requestFocus() binding.childPassword.password.requestFocus()
inputMethodManager.showSoftInput(binding.childPassword.password, 0) inputMethodManager.showSoftInput(binding.childPassword.password, 0)
@ -305,7 +296,7 @@ class NewLoginFragment: DialogFragment() {
binding.childPassword.password.isEnabled = !status.isCheckingPassword binding.childPassword.password.isEnabled = !status.isCheckingPassword
if (status.wasPasswordWrong) { if (status.wasPasswordWrong) {
Toast.makeText(context!!, R.string.login_snackbar_wrong, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.login_snackbar_wrong, Toast.LENGTH_SHORT).show()
binding.childPassword.password.setText("") binding.childPassword.password.setText("")
model.resetPasswordWrong() model.resetPasswordWrong()
@ -314,32 +305,20 @@ class NewLoginFragment: DialogFragment() {
null null
} }
ChildLoginRequiresPremiumStatus -> { ChildLoginRequiresPremiumStatus -> {
if (binding.switcher.displayedChild != CHILD_LOGIN_REQUIRES_PREMIUM) { binding.switcher.openNextWizardScreen(CHILD_LOGIN_REQUIRES_PREMIUM)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = CHILD_LOGIN_REQUIRES_PREMIUM
}
null null
} }
is ParentUserLoginBlockedByCategory -> { is ParentUserLoginBlockedByCategory -> {
if (binding.switcher.displayedChild != PARENT_LOGIN_BLOCKED) { binding.switcher.openNextWizardScreen(PARENT_LOGIN_BLOCKED)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = PARENT_LOGIN_BLOCKED
}
binding.parentLoginBlocked.categoryTitle = status.categoryTitle binding.parentLoginBlocked.categoryTitle = status.categoryTitle
binding.parentLoginBlocked.reason = LoginDialogFragmentModel.formatBlockingReasonForLimitLoginCategory(status.reason, context!!) binding.parentLoginBlocked.reason = LoginDialogFragmentModel.formatBlockingReasonForLimitLoginCategory(status.reason, requireContext())
null null
} }
ParentUserLoginWaitingForSync -> { ParentUserLoginWaitingForSync -> {
if (binding.switcher.displayedChild != WAITING_FOR_SYNC) { binding.switcher.openNextWizardScreen(WAITING_FOR_SYNC)
binding.switcher.setInAnimation(context!!, R.anim.wizard_open_step_in)
binding.switcher.setOutAnimation(context!!, R.anim.wizard_open_step_out)
binding.switcher.displayedChild = WAITING_FOR_SYNC
}
null null
} }
@ -350,6 +329,6 @@ class NewLoginFragment: DialogFragment() {
} }
fun tryCodeLogin(code: ScannedKey) { fun tryCodeLogin(code: ScannedKey) {
model.tryCodeLogin(code, getActivityViewModel(activity!!)) model.tryCodeLogin(code, getActivityViewModel(requireActivity()))
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann * TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -15,8 +15,12 @@
*/ */
package io.timelimit.android.ui.manage.parent.key package io.timelimit.android.ui.manage.parent.key
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import io.timelimit.android.R import io.timelimit.android.R
import io.timelimit.android.async.Threads import io.timelimit.android.async.Threads
@ -24,7 +28,7 @@ import io.timelimit.android.data.model.UserKey
import io.timelimit.android.extensions.showSafe import io.timelimit.android.extensions.showSafe
import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.logic.DefaultAppLogic
class AddUserKeyDialogFragment: ScanKeyDialogFragment() { class AddUserKeyDialogFragment: DialogFragment() {
companion object { companion object {
private const val DIALOG_TAG = "AddUserKeyDialogFragment" private const val DIALOG_TAG = "AddUserKeyDialogFragment"
private const val USER_ID = "userId" private const val USER_ID = "userId"
@ -36,13 +40,39 @@ class AddUserKeyDialogFragment: ScanKeyDialogFragment() {
} }
} }
override fun handleResult(key: ScannedKey?) { private val scanBarcode = registerForActivityResult(ScanBarcode()) { result ->
if (result != null) handleResult(ScannedKey.tryDecode(result))
dismiss()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(requireContext(), theme)
.setTitle(R.string.manage_user_key_add)
.setMessage(R.string.manage_user_key_info)
.setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.generic_go, null)
.create()
.also { dialog ->
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
try {
scanBarcode.launch(null)
} catch (ex: ActivityNotFoundException) {
MissingBarcodeScannerDialogFragment.newInstance().show(parentFragmentManager)
dismiss()
}
}
}
}
private fun handleResult(key: ScannedKey?) {
if (key == null) { if (key == null) {
Toast.makeText(context!!, R.string.manage_user_key_invalid, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.manage_user_key_invalid, Toast.LENGTH_SHORT).show()
} else { } else {
val context = context!!.applicationContext val context = requireContext().applicationContext
val database = DefaultAppLogic.with(context!!).database val database = DefaultAppLogic.with(context!!).database
val userId = arguments!!.getString(USER_ID)!! val userId = requireArguments().getString(USER_ID)!!
Threads.database.execute { Threads.database.execute {
database.runInTransaction { database.runInTransaction {

View file

@ -0,0 +1,57 @@
/*
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.manage.parent.key
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import io.timelimit.android.R
import io.timelimit.android.extensions.showSafe
class MissingBarcodeScannerDialogFragment: DialogFragment() {
companion object {
private const val DIALOG_TAG = "MissingBarcodeScannerDialogFragment"
fun newInstance() = MissingBarcodeScannerDialogFragment()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(requireContext(), theme)
.setTitle(R.string.scan_key_missing_title)
.setMessage(R.string.scan_key_missing_text)
.setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.scan_key_missing_install) { _, _ ->
try {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=de.markusfisch.android.binaryeye")
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} catch (ex: ActivityNotFoundException) {
Toast.makeText(requireContext(), R.string.error_general, Toast.LENGTH_SHORT).show()
}
}
.create()
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
}

View file

@ -0,0 +1,33 @@
/*
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.manage.parent.key
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract
class ScanBarcode: ActivityResultContract<Unit, String?>() {
override fun createIntent(context: Context, input: Unit?): Intent = Intent()
.setPackage("de.markusfisch.android.binaryeye")
.setAction("com.google.zxing.client.android.SCAN")
override fun parseResult(resultCode: Int, intent: Intent?): String? {
return if (resultCode == Activity.RESULT_OK) {
intent?.getStringExtra("SCAN_RESULT")
} else null
}
}

View file

@ -1,102 +0,0 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.manage.parent.key
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import io.timelimit.android.R
abstract class ScanKeyDialogFragment: DialogFragment() {
companion object {
private const val CAN_NOT_SCAN = "canNotScan"
private const val REQ_SCAN = 1
}
private var canNotScan = false
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
try {
startActivityForResult(
Intent()
.setPackage("de.markusfisch.android.binaryeye")
.setAction("com.google.zxing.client.android.SCAN"),
REQ_SCAN
)
} catch (ex: ActivityNotFoundException) {
canNotScan = true
}
} else {
canNotScan = savedInstanceState.getBoolean(CAN_NOT_SCAN)
}
}
final override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
if (canNotScan) {
return AlertDialog.Builder(context!!, theme)
.setTitle(R.string.scan_key_missing_title)
.setMessage(R.string.scan_key_missing_text)
.setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.scan_key_missing_install) { _, _ ->
try {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=de.markusfisch.android.binaryeye")
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} catch (ex: ActivityNotFoundException) {
Toast.makeText(context!!, R.string.error_general, Toast.LENGTH_SHORT).show()
}
}
.create()
} else {
return super.onCreateDialog(savedInstanceState)
}
}
final override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(CAN_NOT_SCAN, canNotScan)
}
final override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQ_SCAN && (!canNotScan)) {
if (resultCode == Activity.RESULT_OK) {
val key = ScannedKey.tryDecode(data?.getStringExtra("SCAN_RESULT") ?: "")
handleResult(key)
}
dismiss()
}
}
abstract fun handleResult(key: ScannedKey?)
}

View file

@ -1064,7 +1064,8 @@
<string name="manage_user_key_title">Benutzerschlüssel</string> <string name="manage_user_key_title">Benutzerschlüssel</string>
<string name="manage_user_key_info">Hiermit kann sich ein Benutzer durch <string name="manage_user_key_info">Hiermit kann sich ein Benutzer durch
das Scannen eines Barcodes anmelden. Der gescannte Code muss von TimeLimit das Scannen eines Barcodes anmelden. Der gescannte Code muss von TimeLimit
generiert worden sein. generiert worden sein. Dafür muss TimeLimit auf einem anderen Gerät als
Schlüsselgenerator eingerichtet sein.
</string> </string>
<string name="manage_user_key_not_enrolled">Es gibt keinen Schlüssel für diesen Benutzer</string> <string name="manage_user_key_not_enrolled">Es gibt keinen Schlüssel für diesen Benutzer</string>
<string name="manage_user_key_is_enrolled">Dieser Benutzer hat den Schlüssel %s</string> <string name="manage_user_key_is_enrolled">Dieser Benutzer hat den Schlüssel %s</string>

View file

@ -1109,10 +1109,11 @@
<string name="manage_user_key_title">User key</string> <string name="manage_user_key_title">User key</string>
<string name="manage_user_key_info">A key is a barcode which allows the user <string name="manage_user_key_info">A key is a barcode which allows the user
to sign in by scanning it. This code must be generated by TimeLimit. to sign in by scanning it. This code must be generated by TimeLimit.
This requires TimeLimit at another device configured as key generator.
</string> </string>
<string name="manage_user_key_not_enrolled">There is no key for this user</string> <string name="manage_user_key_not_enrolled">There is no key for this user</string>
<string name="manage_user_key_is_enrolled">This user has got the key %s</string> <string name="manage_user_key_is_enrolled">This user has got the key %s</string>
<string name="manage_user_key_add">add key</string> <string name="manage_user_key_add">Add Key</string>
<string name="manage_user_key_remove">remove key</string> <string name="manage_user_key_remove">remove key</string>
<string name="manage_user_key_added">The key was added</string> <string name="manage_user_key_added">The key was added</string>
<string name="manage_user_key_invalid">This was no valid TimeLimit key</string> <string name="manage_user_key_invalid">This was no valid TimeLimit key</string>