Skip to content

Commit dc674e2

Browse files
committed
fix service termination on some chinese OEM devices
some chinese OEM devices have aggressive battery optimizations which would kill the service when the overlay option is enabled and the application is swiped away from the recent apps list or after some time has passed, the fix is a litte bit crude for now. the app saves the last remaining timeout to the device's storage every one second and then when the service is killed, it retreives the last saved remaining timeout and then restarts the service. This may put a strain on the device's storage but can be avoided if the overlay is disabled while running on these devices Change-Id: I7dc0b984499d0e23d1fd5e80b642d7b5c93d9634 Signed-off-by: AbdAlMoniem AlHifnawy <hifnawy_moniem@hotmail.com>
1 parent 9897863 commit dc674e2

File tree

8 files changed

+180
-25
lines changed

8 files changed

+180
-25
lines changed

.github/workflows/publish_release.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+
- v1.8.0
1213
- v1.7.5
1314
- v1.7.2
1415
- v1.7.1

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = "com.hifnawy.caffeinate"
1616
minSdk = 24
1717
targetSdk = 35
18-
versionCode = 29
19-
versionName = "1.7.5"
18+
versionCode = 30
19+
versionName = "1.8.0"
2020

2121
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2222
}

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
3232

3333
<category android:name="android.intent.category.LAUNCHER" />
34-
<!-- <category android:name="android.intent.category.DEFAULT" /> -->
34+
<category android:name="android.intent.category.DEFAULT" />
3535
</intent-filter>
3636
</activity>
3737

app/src/main/java/com/hifnawy/caffeinate/services/KeepAwakeService.kt

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,13 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
287287
this@KeepAwakeService.isDimmingEnabled = isDimmingEnabled
288288
this@KeepAwakeService.isWhileLockedEnabled = isWhileLockedEnabled
289289
}
290-
290+
val restartDuration = when (val lastTimeout = intent.getLongExtra(Intent.EXTRA_INTENT, -1L)) {
291+
-1L -> null
292+
else -> lastTimeout.seconds
293+
}
291294
when (keepAwakeServiceAction) {
292295
ACTION_START -> prepareService()
293-
ACTION_RESTART -> restart(caffeinateApplication)
296+
ACTION_RESTART -> restart(caffeinateApplication, restartDuration)
294297
ACTION_STOP -> stopCaffeine()
295298
ACTION_CHANGE_TIMEOUT -> startNextTimeout(caffeinateApplication, debounce = false)
296299
ACTION_CHANGE_DIMMING_ENABLED -> sharedPreferences.isDimmingEnabled = !sharedPreferences.isDimmingEnabled
@@ -305,6 +308,28 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
305308
* This method is triggered when the user swipes away the app from the recent apps list.
306309
* It is responsible for cleaning up resources and stopping the caffeine session.
307310
*
311+
*
312+
* > TODO: Fix service stopping when overlay is shown and the app is swiped away
313+
* from the recent apps list on some chinese devices.
314+
*
315+
* > On some chinese devices, the service is stopped when the user swipes away the app
316+
* from the recent apps list. but the service is not stopped gracefully, so we need
317+
* to stop the service manually. This also seems to only happen when the overlay is
318+
* shown; but if it is not, the service is not stopped for some reason.
319+
*
320+
* > For now there is no way around this limitation except checking the device model
321+
* and gracefully stopping the service only if the overlay is shown. We use the
322+
* isOverlayEnabled flag from the shared preferences to check if the overlay was
323+
* shown or not before the service got killed, since the isOverlayEnabled flag will
324+
* be reset after the service instance is killed.
325+
*
326+
* > For now we can restart the service manually when the user swipes away the app
327+
* from the recent apps list passing the last remaining timeout to the service.
328+
* This is a crude solution, and may put a strain on the device's storage since
329+
* it will store the last remaining timeout in the shared preferences every time
330+
* the service status is updated. which is every one second.
331+
*
332+
*
308333
* @param rootIntent [Intent?] The root Intent that was used to launch the task.
309334
* This may be `null` if the task was removed via the task manager.
310335
*
@@ -313,8 +338,13 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
313338
override fun onTaskRemoved(rootIntent: Intent?) {
314339
super.onTaskRemoved(rootIntent)
315340

316-
Log.d("${this::class.simpleName} service removed from task manager!")
317-
if (sharedPreferences.isOverlayEnabled) stopCaffeine()
341+
Log.d("${this::class.simpleName} service removed from task manager! restarting...")
342+
343+
Intent(this, KeepAwakeService::class.java).apply {
344+
action = ACTION_RESTART.name
345+
putExtra(Intent.EXTRA_INTENT, sharedPreferences.lastRemainingTimeout)
346+
startService(this)
347+
}
318348
}
319349

320350
/**
@@ -383,6 +413,12 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
383413

384414
is ServiceStatus.Stopped -> stopForeground(STOP_FOREGROUND_REMOVE)
385415
}
416+
/* TODO: Remove or find a way to infer the service status without using shared preferences
417+
* because this will put a strain on the device's storage since it will store
418+
* the last remaining timeout in the shared preferences every time the service
419+
* status is updated. which is every one second.
420+
*/
421+
sharedPreferences.isServiceRunning = status is ServiceStatus.Running
386422
}
387423

388424
/**
@@ -912,8 +948,10 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
912948
* ```
913949
*
914950
* @param caffeinateApplication [CaffeinateApplication] The application context.
951+
* @param startTimeout [Duration] The timeout duration to use when starting the service. If `null`, the service will use the previously set timeout.
915952
*/
916-
fun restart(caffeinateApplication: CaffeinateApplication) = toggleState(caffeinateApplication, STATE_START)
953+
fun restart(caffeinateApplication: CaffeinateApplication, startTimeout: Duration? = null) =
954+
toggleState(caffeinateApplication, STATE_START, startTimeout)
917955

918956
/**
919957
* Toggles the state of the KeepAwakeService.
@@ -930,7 +968,7 @@ class KeepAwakeService : Service(), SharedPrefsObserver, ServiceStatusObserver {
930968
fun toggleState(
931969
caffeinateApplication: CaffeinateApplication,
932970
newKeepAwakeServiceState: KeepAwakeServiceState,
933-
startTimeout: Duration? = null,
971+
startTimeout: Duration? = null
934972
): Unit = caffeinateApplication.run {
935973
Log.d("newState: $newKeepAwakeServiceState")
936974
val start = when (newKeepAwakeServiceState) {

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.content.Context
44
import androidx.appcompat.app.AppCompatDelegate
55
import com.hifnawy.caffeinate.CaffeinateApplication
66
import com.hifnawy.caffeinate.services.Observer
7+
import com.hifnawy.caffeinate.services.ServiceStatus
78
import com.hifnawy.caffeinate.ui.CheckBoxItem
89
import com.hifnawy.caffeinate.utils.DurationExtensionFunctions.toFormattedTime
910
import com.hifnawy.caffeinate.utils.DurationExtensionFunctions.toLocalizedFormattedTime
@@ -14,6 +15,8 @@ import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.ENABLE_DI
1415
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.ENABLE_MATERIAL_YOU
1516
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.ENABLE_OVERLAY
1617
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.ENABLE_WHILE_LOCKED
18+
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.IS_SERVICE_RUNNING
19+
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.LAST_REMAINING_TIMEOUT
1720
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.THEME
1821
import com.hifnawy.caffeinate.utils.SharedPrefsManager.SharedPrefsKeys.TIMEOUT_CHECK_BOXES
1922
import com.hifnawy.caffeinate.utils.SharedPrefsManager.Theme.DARK
@@ -90,6 +93,26 @@ class SharedPrefsManager(private val caffeinateApplication: CaffeinateApplicatio
9093
* A [List] of [CheckBoxItem] values indicating the timeouts that should be displayed in the RecyclerView.
9194
*/
9295
TIMEOUT_CHECK_BOXES,
96+
97+
/**
98+
* A [Boolean] value indicating whether the service is running.
99+
*
100+
* > TODO: Remove or find a way to infer the service status without using shared preferences
101+
* because this will put a strain on the device's storage since it will store
102+
* the last remaining timeout in the shared preferences every time the service
103+
* status is updated. which is every one second.
104+
*/
105+
IS_SERVICE_RUNNING,
106+
107+
/**
108+
* A [Long] value indicating the last remaining timeout value stored in shared preferences.
109+
*
110+
* > TODO: Remove or find a way to infer the service status without using shared preferences
111+
* because this will put a strain on the device's storage since it will store
112+
* the last remaining timeout in the shared preferences every time the service
113+
* status is updated. which is every one second.
114+
*/
115+
LAST_REMAINING_TIMEOUT
93116
}
94117

95118
/**
@@ -261,6 +284,51 @@ class SharedPrefsManager(private val caffeinateApplication: CaffeinateApplicatio
261284
checkBoxItem.copy(text = checkBoxItem.duration.toLocalizedFormattedTime(caffeinateApplication.localizedApplicationContext))
262285
}).apply()
263286

287+
/**
288+
* Retrieves or sets whether the service is running.
289+
*
290+
* This property allows the application to determine if the service is currently running. The value is stored in
291+
* shared preferences and persists across sessions.
292+
*
293+
* > TODO: Remove or find a way to infer the service status without using shared preferences
294+
* because this will put a strain on the device's storage since it will store
295+
* the last remaining timeout in the shared preferences every time the service
296+
* status is updated. which is every one second.
297+
*
298+
* @return [Boolean] `true` if the service is running, `false` otherwise.
299+
*/
300+
var isServiceRunning: Boolean
301+
get() = sharedPreferences.getBoolean(IS_SERVICE_RUNNING.name, false)
302+
set(value) {
303+
sharedPreferences.edit().putBoolean(IS_SERVICE_RUNNING.name, value).apply()
304+
val timeout = when (value) {
305+
true -> (caffeinateApplication.lastStatusUpdate as ServiceStatus.Running).remaining.inWholeSeconds
306+
false -> -1L
307+
}
308+
309+
sharedPreferences.edit().putLong(LAST_REMAINING_TIMEOUT.name, timeout).apply()
310+
}
311+
312+
/**
313+
* Retrieves the last remaining timeout value stored in shared preferences.
314+
*
315+
* This property returns the last remaining timeout value stored in shared preferences, which is the value of the
316+
* remaining timeout at the time the service was last stopped. The value is stored in shared preferences and persists
317+
* across sessions.
318+
*
319+
* If the service is not running or the last remaining timeout has not been stored, this property returns `-1`.
320+
*
321+
* > TODO: Remove or find a way to infer the service status without using shared preferences
322+
* because this will put a strain on the device's storage since it will store
323+
* the last remaining timeout in the shared preferences every time the service
324+
* status is updated. which is every one second.
325+
*
326+
* @return [Long] The last remaining timeout value stored in shared preferences, or `-1` if the service is not running
327+
* or the last remaining timeout has not been stored.
328+
*/
329+
val lastRemainingTimeout: Long
330+
get() = sharedPreferences.getLong(LAST_REMAINING_TIMEOUT.name, -1)
331+
264332
/**
265333
* Notifies all registered observers of a change in the shared preferences.
266334
*

app/src/main/res/xml/widget_info.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools"
4-
android:description="@string/app_name"
54
android:configure="com.hifnawy.caffeinate.ui.WidgetConfigurationActivity"
5+
android:description="@string/app_name"
66
android:initialKeyguardLayout="@layout/widget"
77
android:initialLayout="@layout/widget"
88
android:previewImage="@drawable/widget_preview"
@@ -12,4 +12,5 @@
1212
android:targetCellHeight="1"
1313
android:updatePeriodMillis="86400000"
1414
android:widgetCategory="home_screen"
15+
android:widgetFeatures="reconfigurable"
1516
tools:ignore="UnusedAttribute" />
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
* added show overlay option
3+
* fix allow while locked not working as expected
4+
* added configureable widget
5+
* bug fixes and code improvements
6+
* fix service termination on some chinese OEM devices
7+
8+
- added an option to show or hide an overlay with the remaining timeout
9+
on the top right/left of the screen
10+
the overlay option may be required on some devices in order for the app to
11+
continue working in the background
12+
if the overlay option is enabled and the app is closed from the recents
13+
menu, the timeout will be cancelled (seems that this is a restriciton
14+
from android, if there is an overlay enabled and the app is closed,
15+
everything gets terminated)
16+
17+
- fixed issue where the allow while locked toggle worked opposite to the intended behavior
18+
19+
- the widget's background can now be configured to be on or off
20+
21+
- some chinese OEM devices have aggressive battery optimizations which
22+
would kill the service when the overlay option is enabled and the
23+
application is swiped away from the recent apps list or after some time
24+
has passed, the fix is a litte bit crude for now. the app saves the last
25+
remaining timeout to the device's storage every one second and then when
26+
the service is killed, it retreives the last saved remaining timeout and
27+
then restarts the service.
28+
29+
This may put a strain on the device's storage but can be avoided if the
30+
overlay is disabled while running on these devices
31+

git_hooks/get_changelog.sh

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ if [ -z "$latestTag" ] || [ -z "$previousTag" ]; then
1717
exit 1
1818
fi
1919

20-
echo "Generating Changelog..."
21-
while IFS= read -r body && IFS= read -r subject; do
22-
subject_trimmed=$(echo "$subject" | sed -e 's/Change-Id:\s*.*//' | sed -e 's/Signed-off-by:\s*.*//' | sed 's/__END__//' | sed -e 's/^[^a-zA-Z0-9]*//')
23-
body_trimmed=$(echo "$body" | sed -e 's/Change-Id:\s*.*//' | sed -e 's/Signed-off-by:\s*.*//' | sed 's/__END__//' | sed -e 's/^[^a-zA-Z0-9]*//')
20+
echo "Generating Changelog between $previousTag and $latestTag..."
21+
while read commit_hash
22+
do
23+
subject=$(git log --format=%s -n 1 $commit_hash | sed -e 's/Change-Id:\s*.*//' | sed -e 's/Signed-off-by:\s*.*//' | sed -e 's/^[^a-zA-Z0-9]*//')
24+
body=$(git log --format=%b -n 1 $commit_hash | sed -e 's/Change-Id:\s*.*//' | sed -e 's/Signed-off-by:\s*.*//' | sed -e 's/^[^a-zA-Z0-9]*//')
2425

25-
[[ -n "$subject_trimmed" ]] && echo "* $subject_trimmed"
26-
[[ -n "$body_trimmed" ]] && echo "- $body_trimmed"
27-
28-
subjects+=("$subject_trimmed")
29-
bodies+=("$body_trimmed")
30-
done < <(git log "$previousTag".."$latestTag" --pretty=format:"%s%n%b%n__END__")
26+
echo "Commit: $commit_hash"
27+
echo "* $subject"
28+
subjects+=("$subject")
29+
if [[ -n "$body" ]]; then
30+
echo "- $body"
31+
bodies+=("$body")
32+
fi
33+
echo "------------------------------"
34+
done < <(git log "$previousTag".."$latestTag" --pretty=format:"%H")
3135

3236
changeLogs=${#subjects[@]}
3337

@@ -36,12 +40,21 @@ if [ $changeLogs -gt 0 ]; then
3640

3741
echo "" > "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
3842

39-
for subject in "${subjects[@]}"; do
40-
[[ -n "$subject" ]] && echo "* $subject" >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
43+
for index in $(seq ${#subjects[@]} -1 1); do
44+
subject=${subjects[$index-1]}
45+
if [[ -n "$subject" ]]; then
46+
echo "* $subject" | sed '2,$s/^/ /' >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
47+
fi
4148
done
4249

43-
for body in "${bodies[@]}"; do
44-
[[ -n "$body" ]] && echo "- $body" >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
50+
echo >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
51+
52+
for index in $(seq ${#bodies[@]} -1 1); do
53+
body=${bodies[$index-1]}
54+
if [[ -n "$body" ]]; then
55+
echo "- $body" | sed '2,$s/^/ /' >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
56+
echo >> "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/$versionCode.txt"
57+
fi
4558
done
4659

4760
currentCommitHash=$(git rev-parse HEAD)
@@ -53,12 +66,15 @@ if [ $changeLogs -gt 0 ]; then
5366
echo
5467

5568
git add "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/"
56-
git commit -sm "updated $changeLogs change logs(s)"
69+
git commit -sm "updated $changeLogs change log(s)"
5770
else
5871
echo "commit '$currentCommitHash' is not on the remote branch, amending..."
5972
echo
6073

6174
git add "$gitTopLevel/fastlane/metadata/android/en-US/changeLogs/"
6275
git commit --amend --no-edit
6376
fi
77+
else
78+
echo "No / $changeLogs change log(s) found between $previousTag and $latestTag"
6479
fi
80+

0 commit comments

Comments
 (0)