Skip to content

Commit ad51f88

Browse files
committed
fixed TimeoutsSelectionFragment crashing when changing languages
Change-Id: I3faa32c951f65caf83cef72d0f7585cd14515540 Signed-off-by: AbdAlMoniem AlHifnawy <hifnawy_moniem@hotmail.com>
1 parent e98fd31 commit ad51f88

File tree

12 files changed

+291
-221
lines changed

12 files changed

+291
-221
lines changed

.github/workflows/publish_release_by_tag.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
required: true
1010
type: choice
1111
options:
12+
- v2.1.0
1213
- v2.0.3
1314
- v2.0.2
1415
- v2.0.1

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ android {
8787
minSdk = 24
8888
compileSdk = 35
8989
targetSdk = 35
90-
versionCode = 34
91-
versionName = "2.0.3"
90+
versionCode = 35
91+
versionName = "2.1.0"
9292

9393
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
9494
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.hifnawy.caffeinate.utils
2+
3+
import android.content.res.Resources
4+
5+
/**
6+
* Provides extension functions for [Int] to facilitate common operations.
7+
*
8+
* This object contains utility functions that extend the functionality of the [Int] class.
9+
* These functions provide convenient methods for performing operations on integers.
10+
*
11+
* @author AbdAlMoniem AlHifnawy
12+
*/
13+
object IntExtensionFunctions {
14+
15+
/**
16+
* Converts an integer value to a size in display pixels (DP).
17+
*
18+
* This property multiplies the integer value by the device's display density
19+
* and returns the result as an integer. It is a convenience method for converting
20+
* a size in DP to a size in pixels.
21+
*
22+
* @receiver [Int] The integer value to be converted.
23+
* @return [Int] The size in DP, as an integer.
24+
*/
25+
val Int.dp: Int
26+
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
27+
}

app/src/main/java/com/hifnawy/caffeinate/utils/ViewExtensionFunctions.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.hifnawy.caffeinate.utils
22

33
import android.graphics.Rect
44
import android.view.View
5+
import com.hifnawy.caffeinate.utils.IntExtensionFunctions.dp
56

67
/**
78
* Utility functions for working with [View].
@@ -27,6 +28,49 @@ object ViewExtensionFunctions {
2728
visibility = if (value) View.VISIBLE else View.GONE
2829
}
2930

31+
/**
32+
* A utility property to set and get the height of a view in pixels.
33+
*
34+
* @return [Int] The height of the view in pixels.
35+
*/
36+
var View.viewHeight: Int
37+
get() = layoutParams.height
38+
set(value) {
39+
if (value <= 0) layoutParams.width = 0.dp
40+
layoutParams.height = value
41+
requestLayout()
42+
}
43+
44+
/**
45+
* A utility property that returns the height of the window in display pixels (DP).
46+
*
47+
* This property calculates the height of the window by taking into account the display height
48+
* and the size of the top and bottom system bars. It takes into account the display density
49+
* and returns the result as an integer.
50+
*
51+
* @return [Int] The height of the window in DP, as an integer.
52+
*/
53+
val View.windowHeight: Int
54+
get() {
55+
val height = resources.displayMetrics.heightPixels
56+
57+
with(rootWindowInsets) {
58+
@Suppress("DEPRECATION")
59+
val topInset = when {
60+
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(android.view.WindowInsets.Type.systemBars()).top
61+
else -> systemWindowInsetTop
62+
}
63+
64+
@Suppress("DEPRECATION")
65+
val bottomInset = when {
66+
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(android.view.WindowInsets.Type.systemBars()).bottom
67+
else -> systemWindowInsetBottom
68+
}
69+
70+
return height + topInset + bottomInset
71+
}
72+
}
73+
3074
/**
3175
* Sets a listener to be called when the size of the view changes.
3276
*

app/src/main/java/com/hifnawy/caffeinate/view/AboutFragment.kt

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ package com.hifnawy.caffeinate.view
33
import android.annotation.SuppressLint
44
import android.content.Intent
55
import android.net.Uri
6-
import android.os.Build
76
import android.os.Bundle
87
import android.transition.Explode
98
import android.transition.TransitionManager
109
import android.view.HapticFeedbackConstants
1110
import android.view.LayoutInflater
1211
import android.view.View
1312
import android.view.ViewGroup
14-
import android.view.WindowInsets
1513
import android.view.animation.AnticipateOvershootInterpolator
1614
import androidx.core.view.children
1715
import androidx.lifecycle.lifecycleScope
@@ -23,6 +21,7 @@ import com.hifnawy.caffeinate.BuildConfig
2321
import com.hifnawy.caffeinate.R
2422
import com.hifnawy.caffeinate.databinding.FragmentAboutBinding
2523
import com.hifnawy.caffeinate.utils.ViewExtensionFunctions.isVisible
24+
import com.hifnawy.caffeinate.utils.ViewExtensionFunctions.windowHeight
2625
import kotlinx.coroutines.delay
2726
import kotlinx.coroutines.launch
2827
import com.google.android.material.R as materialR
@@ -84,48 +83,6 @@ class AboutFragment : BottomSheetDialogFragment() {
8483
}
8584
}
8685

87-
/**
88-
* A utility property that converts a given integer value to a size in display pixels (DP).
89-
*
90-
* This property multiplies the given integer value by the device's display density and
91-
* returns the result as an integer. It is a convenience method for converting a size
92-
* in DP to a size in pixels.
93-
*
94-
* @return [Int] The size in DP, as an integer.
95-
*/
96-
private val Int.dp: Int
97-
get() = (this * resources.displayMetrics.density).toInt()
98-
99-
/**
100-
* A utility property that returns the height of the window in display pixels (DP).
101-
*
102-
* This property calculates the height of the window by taking into account the display height
103-
* and the size of the top and bottom system bars. It takes into account the display density
104-
* and returns the result as an integer.
105-
*
106-
* @return [Int] The height of the window in DP, as an integer.
107-
*/
108-
private val windowHeight: Int
109-
get() {
110-
val height = resources.displayMetrics.heightPixels
111-
112-
with(requireView().rootWindowInsets) {
113-
@Suppress("DEPRECATION")
114-
val topInset = when {
115-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).top
116-
else -> systemWindowInsetTop
117-
}
118-
119-
@Suppress("DEPRECATION")
120-
val bottomInset = when {
121-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).bottom
122-
else -> systemWindowInsetBottom
123-
}
124-
125-
return height + topInset + bottomInset
126-
}
127-
}
128-
12986
/**
13087
* Called to have the fragment instantiate its user interface view.
13188
* This is optional, and non-graphical fragments can return null. This will be called between
@@ -198,7 +155,7 @@ class AboutFragment : BottomSheetDialogFragment() {
198155
skipCollapsed = true
199156
isFitToContents = true
200157
dismissWithAnimation = true
201-
peekHeight = dialog?.layoutParams?.height ?: (windowHeight * 3 / 5)
158+
peekHeight = dialog?.layoutParams?.height ?: (binding.root.windowHeight * 3 / 5)
202159
state = BottomSheetBehavior.STATE_EXPANDED
203160

204161
addBottomSheetCallback(bottomSheetCallback)

app/src/main/java/com/hifnawy/caffeinate/view/CheckBoxAdapter.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.hifnawy.caffeinate.view
22

3-
import android.content.res.Resources
43
import android.transition.Explode
54
import android.transition.TransitionManager
65
import android.view.HapticFeedbackConstants
@@ -15,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView
1514
import com.hifnawy.caffeinate.R
1615
import com.hifnawy.caffeinate.databinding.TimeoutCheckboxItemBinding
1716
import com.hifnawy.caffeinate.utils.DurationExtensionFunctions.toLocalizedFormattedTime
17+
import com.hifnawy.caffeinate.utils.IntExtensionFunctions.dp
1818
import com.hifnawy.caffeinate.utils.ViewExtensionFunctions.isVisible
1919
import com.hifnawy.caffeinate.view.CheckBoxAdapter.ModificationType.ITEM_CHANGED_ALL
2020
import com.hifnawy.caffeinate.view.CheckBoxAdapter.ModificationType.ITEM_CHANGED_SINGLE
@@ -136,18 +136,6 @@ class CheckBoxAdapter(
136136
*/
137137
private val mainCoroutineScope by lazy { CoroutineScope(Dispatchers.Main) }
138138

139-
/**
140-
* A utility property that converts a given integer value to a size in display pixels (DP).
141-
*
142-
* This property multiplies the given integer value by the device's display density and
143-
* returns the result as an integer. It is a convenience method for converting a size
144-
* in DP to a size in pixels.
145-
*
146-
* @return [Int] The size in DP, as an integer.
147-
*/
148-
private val Int.dp: Int
149-
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
150-
151139
/**
152140
* The RecyclerView instance that this adapter is attached to.
153141
*

app/src/main/java/com/hifnawy/caffeinate/view/MainActivity.kt

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,23 +1119,25 @@ class MainActivity : AppCompatActivity(), SharedPrefsObserver, ServiceStatusObse
11191119
val timeoutChoiceClickListener = View.OnClickListener { view ->
11201120
view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
11211121

1122-
TimeoutsSelectionFragment.getInstance(caffeinateApplication) { checkBoxItems ->
1123-
caffeinateApplication.run {
1124-
timeoutCheckBoxes.clear()
1125-
timeoutCheckBoxes.addAll(checkBoxItems)
1126-
1127-
timeoutChoiceSubTextTextView.text = timeoutCheckBoxes.enabledDurations
1128-
1129-
// find out if the current timeout is still enabled, if not, set it to the first enabled timeout
1130-
checkBoxItems.find { checkBoxItem -> checkBoxItem.duration == timeout && !checkBoxItem.isChecked }.let {
1131-
when (lastStatusUpdate) {
1132-
is ServiceStatus.Running if it != null -> KeepAwakeService.startNextTimeout(this, debounce = false)
1133-
else -> timeout = firstTimeout
1134-
}
1122+
TimeoutsSelectionFragment.newInstance.apply {
1123+
selectionCallback = { checkBoxItems ->
1124+
caffeinateApplication.run {
1125+
timeoutCheckBoxes.clear()
1126+
timeoutCheckBoxes.addAll(checkBoxItems)
1127+
1128+
timeoutChoiceSubTextTextView.text = timeoutCheckBoxes.enabledDurations
1129+
1130+
// find out if the current timeout is still enabled, if not, set it to the first enabled timeout
1131+
checkBoxItems.find { checkBoxItem -> checkBoxItem.duration == timeout && !checkBoxItem.isChecked }?.let {
1132+
when (lastStatusUpdate) {
1133+
is ServiceStatus.Running -> KeepAwakeService.startNextTimeout(this, debounce = false)
1134+
else -> timeout = firstTimeout
1135+
}
1136+
} ?: run { timeout = firstTimeout }
1137+
1138+
sharedPreferences.timeoutCheckBoxes.clear()
1139+
sharedPreferences.timeoutCheckBoxes.addAll(checkBoxItems)
11351140
}
1136-
1137-
sharedPreferences.timeoutCheckBoxes.clear()
1138-
sharedPreferences.timeoutCheckBoxes.addAll(checkBoxItems)
11391141
}
11401142
}.show(supportFragmentManager, TimeoutsSelectionFragment::class.simpleName)
11411143
}

app/src/main/java/com/hifnawy/caffeinate/view/PrivacyPolicyFragment.kt

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.hifnawy.caffeinate.view
22

33
import android.content.Context
4-
import android.os.Build
54
import android.os.Bundle
65
import android.text.Html
76
import android.text.method.LinkMovementMethod
87
import android.view.LayoutInflater
98
import android.view.View
109
import android.view.ViewGroup
11-
import android.view.WindowInsets
1210
import android.widget.FrameLayout
1311
import androidx.annotation.ColorInt
1412
import androidx.core.text.HtmlCompat
@@ -20,6 +18,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
2018
import com.google.android.material.color.MaterialColors
2119
import com.hifnawy.caffeinate.R
2220
import com.hifnawy.caffeinate.databinding.FragmentPrivacyPolicyBinding
21+
import com.hifnawy.caffeinate.utils.ViewExtensionFunctions.windowHeight
2322
import org.jsoup.Jsoup
2423
import java.io.BufferedReader
2524
import java.io.InputStreamReader
@@ -55,36 +54,6 @@ class PrivacyPolicyFragment : BottomSheetDialogFragment() {
5554
*/
5655
private val binding by lazy { FragmentPrivacyPolicyBinding.inflate(layoutInflater) }
5756

58-
/**
59-
* A utility property that returns the height of the window in display pixels (DP).
60-
*
61-
* This property calculates the height of the window by taking into account the display height
62-
* and the size of the top and bottom system bars. It takes into account the display density
63-
* and returns the result as an integer.
64-
*
65-
* @return [Int] The height of the window in DP, as an integer.
66-
*/
67-
private val windowHeight: Int
68-
get() {
69-
val height = resources.displayMetrics.heightPixels
70-
71-
with(requireView().rootWindowInsets) {
72-
@Suppress("DEPRECATION")
73-
val topInset = when {
74-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).top
75-
else -> systemWindowInsetTop
76-
}
77-
78-
@Suppress("DEPRECATION")
79-
val bottomInset = when {
80-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()).bottom
81-
else -> systemWindowInsetBottom
82-
}
83-
84-
return height + topInset + bottomInset
85-
}
86-
}
87-
8857
/**
8958
* Called to have the fragment instantiate its user interface view.
9059
* This is optional, and non-graphical fragments can return null. This will be called between
@@ -160,12 +129,12 @@ class PrivacyPolicyFragment : BottomSheetDialogFragment() {
160129
override fun onCreateDialog(savedInstanceState: Bundle?) = BottomSheetDialog(requireContext()).apply {
161130
setOnShowListener { dialogInterface ->
162131
val dialog = (dialogInterface as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
163-
dialog?.layoutParams?.height = windowHeight
132+
dialog?.layoutParams?.height = binding.root.windowHeight
164133

165134
behavior.apply {
166135
isFitToContents = true
167136
dismissWithAnimation = true
168-
peekHeight = windowHeight * 2 / 5
137+
peekHeight = binding.root.windowHeight * 2 / 5
169138
state = BottomSheetBehavior.STATE_COLLAPSED
170139
}
171140
}

0 commit comments

Comments
 (0)