Skip to content

Commit

Permalink
Replace time duration picker dialog for screen lock timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
cody-signal authored and greyson-signal committed Feb 14, 2023
1 parent db65edb commit 2305015
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 48 deletions.
2 changes: 2 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,6 @@ dependencies {
implementation libs.greenrobot.eventbus
implementation libs.waitingdots
implementation libs.google.zxing.android.integration
implementation libs.time.duration.picker
implementation libs.google.zxing.core
implementation libs.google.flexbox
implementation (libs.subsampling.scale.image.view) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.thoughtcrime.securesms.components

import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.databinding.TimeDurationPickerDialogBinding
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes

/**
* Time duration dialog for selection a duration of hours and minutes. Currently
* designed specifically for screen lock but could easily be generalized in the future
* if needed.
*
* Uses [setFragmentResult] to pass the provided duration back in milliseconds.
*/
class TimeDurationPickerDialog : DialogFragment(), NumericKeyboardView.Listener {

private var _binding: TimeDurationPickerDialogBinding? = null
private val binding: TimeDurationPickerDialogBinding
get() = _binding!!

private var duration: String = "0000"
private var full: Boolean = false

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = TimeDurationPickerDialogBinding.inflate(layoutInflater)

binding.durationKeyboard.listener = this

setDuration(requireArguments().getLong(ARGUMENT_DURATION).milliseconds)

return MaterialAlertDialogBuilder(requireContext())
.setView(binding.root)
.setPositiveButton(R.string.TimeDurationPickerDialog_positive_button) { _, _ ->
setFragmentResult(
RESULT_DURATION,
bundleOf(
RESULT_KEY_DURATION_MILLISECONDS to getDuration().inWholeMilliseconds
)
)
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}

override fun onKeyPress(keyCode: Int) {
if (full && keyCode != -1) {
return
}

duration = if (keyCode == -1) {
"0" + duration.substring(0, 3)
} else {
duration.substring(1) + keyCode
}

updateDuration()
}

private fun updateDuration() {
binding.durationHour.text = duration.substring(0, 2)
binding.durationMinute.text = duration.substring(2)
full = duration.toInt() > 1000
}

private fun setDuration(duration: Duration) {
val hour = duration.inWholeMinutes / 60
val minute = duration.inWholeMinutes.mod(60)
this.duration = String.format("%02d%02d", hour, minute)
updateDuration()
}

private fun getDuration(): Duration {
val hours = duration.substring(0, 2).toInt()
val minutes = duration.substring(2).toInt()

return hours.hours.plus(minutes.minutes)
}

companion object {
const val RESULT_DURATION = "RESULT_DURATION"
const val RESULT_KEY_DURATION_MILLISECONDS = "RESULT_KEY_DURATION_MILLISECONDS"

private const val ARGUMENT_DURATION = "ARGUMENT_DURATION"

fun create(duration: Duration): TimeDurationPickerDialog {
return TimeDurationPickerDialog().apply {
arguments = bundleOf(
ARGUMENT_DURATION to duration.inWholeMilliseconds
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,15 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import mobi.upod.timedurationpicker.TimeDurationPicker
import mobi.upod.timedurationpicker.TimeDurationPickerDialog
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.BiometricDeviceAuthentication
import org.thoughtcrime.securesms.BiometricDeviceLockContract
import org.thoughtcrime.securesms.PassphraseChangeActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.TimeDurationPickerDialog
import org.thoughtcrime.securesms.components.settings.ClickPreference
import org.thoughtcrime.securesms.components.settings.ClickPreferenceViewHolder
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
Expand All @@ -56,8 +54,9 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import java.lang.Integer.max
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

private val TAG = Log.tag(PrivacySettingsFragment::class.java)

Expand Down Expand Up @@ -257,15 +256,13 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
clickPref(
title = DSLSettingsText.from(R.string.preferences__inactivity_timeout_interval),
onClick = {
TimeDurationPickerDialog(
context,
{ _: TimeDurationPicker?, duration: Long ->
val timeoutMinutes = max(TimeUnit.MILLISECONDS.toMinutes(duration).toInt(), 1)
viewModel.setObsoletePasswordTimeout(timeoutMinutes)
},
0,
TimeDurationPicker.HH_MM
).show()
childFragmentManager.clearFragmentResult(TimeDurationPickerDialog.RESULT_DURATION)
childFragmentManager.clearFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION)
childFragmentManager.setFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION, this@PrivacySettingsFragment) { _, bundle ->
val timeout = bundle.getLong(TimeDurationPickerDialog.RESULT_KEY_DURATION_MILLISECONDS).milliseconds.inWholeMinutes.toInt()
viewModel.setObsoletePasswordTimeout(max(timeout, 1))
}
TimeDurationPickerDialog.create(state.screenLockActivityTimeout.seconds).show(childFragmentManager, null)
}
)
} else {
Expand All @@ -292,15 +289,12 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
summary = DSLSettingsText.from(getScreenLockInactivityTimeoutSummary(state.screenLockActivityTimeout)),
isEnabled = isKeyguardSecure && state.screenLock,
onClick = {
TimeDurationPickerDialog(
context,
{ _: TimeDurationPicker?, duration: Long ->
val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(duration)
viewModel.setScreenLockTimeout(timeoutSeconds)
},
0,
TimeDurationPicker.HH_MM
).show()
childFragmentManager.clearFragmentResult(TimeDurationPickerDialog.RESULT_DURATION)
childFragmentManager.clearFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION)
childFragmentManager.setFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION, this@PrivacySettingsFragment) { _, bundle ->
viewModel.setScreenLockTimeout(bundle.getLong(TimeDurationPickerDialog.RESULT_KEY_DURATION_MILLISECONDS).milliseconds.inWholeSeconds)
}
TimeDurationPickerDialog.create(state.screenLockActivityTimeout.seconds).show(childFragmentManager, null)
}
)
}
Expand Down
22 changes: 11 additions & 11 deletions app/src/main/res/layout/numeric_keyboard_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__1"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_4"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_2"
app:layout_constraintStart_toStartOf="parent"
Expand All @@ -29,7 +29,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__2"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_4"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_3"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_1"
Expand All @@ -43,7 +43,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__3"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_2"
Expand All @@ -57,7 +57,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__4"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_7"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_5"
app:layout_constraintStart_toStartOf="parent"
Expand All @@ -71,7 +71,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__5"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_7"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_6"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_4"
Expand All @@ -85,7 +85,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__6"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_5"
Expand All @@ -99,7 +99,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__7"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_0"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_8"
app:layout_constraintStart_toStartOf="parent"
Expand All @@ -113,7 +113,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__8"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_0"
app:layout_constraintEnd_toStartOf="@id/numeric_keyboard_9"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_7"
Expand All @@ -127,7 +127,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__9"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toTopOf="@id/numeric_keyboard_0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/numeric_keyboard_8"
Expand All @@ -141,7 +141,7 @@
android:gravity="center"
android:text="@string/NumericKeyboardView__0"
android:textAppearance="@style/Signal.Text.TitleLarge"
android:textColor="@color/signal_light_colorOnSurface"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/numeric_keyboard_8"
app:layout_constraintStart_toStartOf="@id/numeric_keyboard_8"
Expand All @@ -159,6 +159,6 @@
app:layout_constraintStart_toStartOf="@id/numeric_keyboard_9"
app:layout_constraintTop_toBottomOf="@id/numeric_keyboard_9"
app:srcCompat="@drawable/ic_backspace_24"
app:tint="@color/signal_light_colorOnSurface" />
app:tint="@color/signal_colorOnSurface" />

</merge>
79 changes: 79 additions & 0 deletions app/src/main/res/layout/time_duration_picker_dialog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="ltr">


<LinearLayout
android:id="@+id/duration_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/signal_colorSurfaceVariant"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingVertical="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<TextView
android:id="@+id/duration_hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@style/Signal.Text.HeadlineLarge"
android:textColor="@color/signal_colorOnSurfaceVariant"
tools:text="01" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/TimeDurationPickerDialog_single_letter_hour_abbreviation"
android:textAppearance="@style/Signal.Text.BodySmall"
android:textColor="@color/signal_colorOnSurfaceVariant" />

<TextView
android:id="@+id/duration_minute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="center_horizontal"
android:textAppearance="@style/Signal.Text.HeadlineLarge"
android:textColor="@color/signal_colorOnSurfaceVariant"
tools:text="01" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/TimeDurationPickerDialog_single_letter_minute_abbreviation"
android:textAppearance="@style/Signal.Text.BodySmall"
android:textColor="@color/signal_colorOnSurfaceVariant" />

</LinearLayout>

<TextView
android:id="@+id/duration_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:paddingHorizontal="16dp"
android:text="@string/TimeDurationPickerDialog_minimum_duration_warning"
android:textColor="@color/signal_colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/duration_layout" />

<org.thoughtcrime.securesms.components.NumericKeyboardView
android:id="@+id/duration_keyboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/duration_header" />

</androidx.constraintlayout.widget.ConstraintLayout>
10 changes: 10 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5605,5 +5605,15 @@
<!-- Option to delete username displayed as a list item in a dialog -->
<string name="UsernameEditDialog__delete_username">Delete username</string>

<!-- Time duration picker -->
<!-- Shown in a time duration picker for selecting duration in hours and minutes, label shown after the user input value for hour, e.g., 12h -->
<string name="TimeDurationPickerDialog_single_letter_hour_abbreviation">h</string>
<!-- Shown in a time duration picker for selecting duration in hours and minutes, label shown after the user input value for minute, e.g., 24m -->
<string name="TimeDurationPickerDialog_single_letter_minute_abbreviation">m</string>
<!-- Shown in a time duration picker for selecting duration in hours and minutes, label for button that will apply the setting -->
<string name="TimeDurationPickerDialog_positive_button">Set</string>
<!-- Shown in a time duration picker for selecting duration in hours and minutes, helper text indicating minimum allowable duration -->
<string name="TimeDurationPickerDialog_minimum_duration_warning">Minimum time before screen lock applies is 1 minute.</string>

<!-- EOF -->
</resources>

0 comments on commit 2305015

Please sign in to comment.