Skip to content

Commit 4021782

Browse files
authored
feat: Add battery optimization handling and wake lock management in MainActivity and YureSensorService (#11)
1 parent 459370a commit 4021782

File tree

3 files changed

+95
-0
lines changed

3 files changed

+95
-0
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
77
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
88
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
9+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
10+
<uses-permission android:name="android.permission.WAKE_LOCK" />
911

1012
<application
1113
android:allowBackup="true"

app/src/main/java/com/sasakulab/yure_android_client/MainActivity.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package com.sasakulab.yure_android_client
22

33
import android.content.Context
44
import android.content.Intent
5+
import android.net.Uri
56
import android.os.Build
67
import android.os.Bundle
8+
import android.os.PowerManager
9+
import android.provider.Settings
710
import androidx.activity.ComponentActivity
811
import androidx.activity.compose.setContent
912
import androidx.activity.enableEdgeToEdge
@@ -63,10 +66,12 @@ class MainActivity : ComponentActivity() {
6366
serverUrl = getServerUrl(),
6467
bufferSize = getBufferSize(),
6568
sensorData = sensorDataBuffer,
69+
isBatteryOptimized = !isBatteryOptimizationIgnored(),
6670
onStartClick = { startSharing() },
6771
onStopClick = { stopSharing() },
6872
onServerUrlChanged = { saveServerUrl(it) },
6973
onBufferSizeChanged = { saveBufferSize(it) },
74+
onRequestBatteryOptimization = { requestIgnoreBatteryOptimization() },
7075
modifier = Modifier.padding(innerPadding)
7176
)
7277
}
@@ -115,6 +120,27 @@ class MainActivity : ComponentActivity() {
115120
prefs.edit().putInt("bufferSize", size).apply()
116121
}
117122

123+
// Check if battery optimization is ignored
124+
private fun isBatteryOptimizationIgnored(): Boolean {
125+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
126+
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
127+
return powerManager.isIgnoringBatteryOptimizations(packageName)
128+
}
129+
return true
130+
}
131+
132+
// Request to ignore battery optimization
133+
private fun requestIgnoreBatteryOptimization() {
134+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
135+
if (!isBatteryOptimizationIgnored()) {
136+
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
137+
data = Uri.parse("package:$packageName")
138+
}
139+
startActivity(intent)
140+
}
141+
}
142+
}
143+
118144
// Start to share accelerometer data
119145
private fun startSharing() {
120146
val serviceIntent = Intent(this, YureSensorService::class.java).apply {
@@ -161,10 +187,12 @@ fun YureScreen(
161187
serverUrl: String,
162188
bufferSize: Int,
163189
sensorData: List<AccelerationData>,
190+
isBatteryOptimized: Boolean,
164191
onStartClick: () -> Unit,
165192
onStopClick: () -> Unit,
166193
onServerUrlChanged: (String) -> Unit,
167194
onBufferSizeChanged: (Int) -> Unit,
195+
onRequestBatteryOptimization: () -> Unit,
168196
modifier: Modifier = Modifier
169197
) {
170198
var showServerUrlDialog by remember { mutableStateOf(false) }
@@ -235,6 +263,40 @@ fun YureScreen(
235263

236264
Spacer(modifier = Modifier.height(32.dp))
237265

266+
// Battery optimization warning
267+
if (isBatteryOptimized) {
268+
Card(
269+
modifier = Modifier.fillMaxWidth(),
270+
colors = CardDefaults.cardColors(
271+
containerColor = MaterialTheme.colorScheme.errorContainer
272+
)
273+
) {
274+
Column(
275+
modifier = Modifier.padding(16.dp)
276+
) {
277+
Text(
278+
text = "Battery Optimization Enabled",
279+
style = MaterialTheme.typography.titleSmall,
280+
color = MaterialTheme.colorScheme.onErrorContainer
281+
)
282+
Spacer(modifier = Modifier.height(8.dp))
283+
Text(
284+
text = "Background operation may stop after hours. Please disable battery optimization for continuous operation.",
285+
style = MaterialTheme.typography.bodySmall,
286+
color = MaterialTheme.colorScheme.onErrorContainer
287+
)
288+
Spacer(modifier = Modifier.height(8.dp))
289+
Button(
290+
onClick = onRequestBatteryOptimization,
291+
modifier = Modifier.fillMaxWidth()
292+
) {
293+
Text("Disable Battery Optimization")
294+
}
295+
}
296+
}
297+
Spacer(modifier = Modifier.height(16.dp))
298+
}
299+
238300
Button(
239301
onClick = { showServerUrlDialog = true },
240302
enabled = !isSharing,

app/src/main/java/com/sasakulab/yure_android_client/YureSensorService.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import android.hardware.SensorEventListener
1313
import android.hardware.SensorManager
1414
import android.os.Build
1515
import android.os.IBinder
16+
import android.os.PowerManager
1617
import androidx.core.app.NotificationCompat
1718
import com.google.gson.Gson
1819
import okhttp3.*
@@ -31,6 +32,7 @@ class YureSensorService : Service(), SensorEventListener {
3132
private val gson = Gson()
3233
private lateinit var yureId: String
3334
private val bufferLock = Any()
35+
private var wakeLock: PowerManager.WakeLock? = null
3436

3537
override fun onCreate() {
3638
super.onCreate()
@@ -45,6 +47,15 @@ class YureSensorService : Service(), SensorEventListener {
4547
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
4648
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
4749
?: sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
50+
51+
// Acquire WakeLock to keep CPU running
52+
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
53+
wakeLock = powerManager.newWakeLock(
54+
PowerManager.PARTIAL_WAKE_LOCK,
55+
"YureSensorService::WakeLock"
56+
).apply {
57+
acquire(10*60*60*1000L /*10 hours*/)
58+
}
4859
}
4960

5061
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -204,5 +215,25 @@ class YureSensorService : Service(), SensorEventListener {
204215
}
205216

206217
webSocket?.close(1000, "Service destroyed")
218+
219+
// Release WakeLock
220+
wakeLock?.let {
221+
if (it.isHeld) {
222+
it.release()
223+
}
224+
}
225+
}
226+
227+
override fun onTaskRemoved(rootIntent: Intent?) {
228+
super.onTaskRemoved(rootIntent)
229+
// Restart service when task is removed
230+
val restartServiceIntent = Intent(applicationContext, YureSensorService::class.java).apply {
231+
setPackage(packageName)
232+
}
233+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
234+
startForegroundService(restartServiceIntent)
235+
} else {
236+
startService(restartServiceIntent)
237+
}
207238
}
208239
}

0 commit comments

Comments
 (0)