mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 13:28:12 -04:00
Add ignore battery optimization check for Android clipboard clear (#1157)
This commit is contained in:
committed by
Leendert de Borst
parent
056f8e97e9
commit
ab740c093f
@@ -4,6 +4,7 @@
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
@@ -19,7 +19,6 @@ class ClipboardClearReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
Log.d(TAG, "Received broadcast to clear clipboard")
|
||||
|
||||
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboardManager.clearPrimaryClip()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.aliasvault.app.nativevaultmanager
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.core.net.toUri
|
||||
@@ -701,6 +702,60 @@ class NativeVaultManager(reactContext: ReactApplicationContext) :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app is ignoring battery optimizations.
|
||||
* @param promise The promise to resolve with boolean result
|
||||
*/
|
||||
@ReactMethod
|
||||
override fun isIgnoringBatteryOptimizations(promise: Promise?) {
|
||||
try {
|
||||
val isIgnoring = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
val powerManager = reactApplicationContext.getSystemService(android.content.Context.POWER_SERVICE) as PowerManager
|
||||
powerManager.isIgnoringBatteryOptimizations(reactApplicationContext.packageName)
|
||||
} else {
|
||||
true // Pre-Android 6.0 doesn't have battery optimization
|
||||
}
|
||||
Log.d(TAG, "Is ignoring battery optimizations: $isIgnoring")
|
||||
promise?.resolve(isIgnoring)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking battery optimization status", e)
|
||||
promise?.reject("ERR_BATTERY_OPTIMIZATION_CHECK", "Failed to check battery optimization status: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request battery optimization exemption by opening system settings.
|
||||
* @param promise The promise to resolve
|
||||
*/
|
||||
@ReactMethod
|
||||
override fun requestIgnoreBatteryOptimizations(promise: Promise?) {
|
||||
try {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
val powerManager = reactApplicationContext.getSystemService(android.content.Context.POWER_SERVICE) as PowerManager
|
||||
|
||||
if (!powerManager.isIgnoringBatteryOptimizations(reactApplicationContext.packageName)) {
|
||||
Log.d(TAG, "Requesting battery optimization exemption via system settings")
|
||||
val intent = Intent().apply {
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = Uri.parse("package:${reactApplicationContext.packageName}")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
reactApplicationContext.startActivity(intent)
|
||||
promise?.resolve("Battery optimization exemption request sent - user will be taken to settings")
|
||||
} else {
|
||||
Log.d(TAG, "App is already ignoring battery optimizations")
|
||||
promise?.resolve("App is already ignoring battery optimizations")
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Battery optimization not applicable on this Android version")
|
||||
promise?.resolve("Battery optimization not applicable on this Android version")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error requesting battery optimization exemption", e)
|
||||
promise?.reject("ERR_BATTERY_OPTIMIZATION_REQUEST", "Failed to request battery optimization exemption: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auth methods.
|
||||
* @param promise The promise to resolve
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
const { t } = useTranslation();
|
||||
const { getClipboardClearTimeout, setClipboardClearTimeout } = useAuth();
|
||||
const [selectedTimeout, setSelectedTimeout] = useState<number>(10);
|
||||
const [canScheduleExactAlarms, setCanScheduleExactAlarms] = useState<boolean>(true);
|
||||
const [isIgnoringBatteryOptimizations, setIsIgnoringBatteryOptimizations] = useState<boolean>(true);
|
||||
const appState = useRef(AppState.currentState);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -40,30 +40,30 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
};
|
||||
|
||||
/**
|
||||
* Check exact alarm permission status on Android.
|
||||
* Check battery optimization exemption status on Android.
|
||||
*/
|
||||
const checkExactAlarmPermission = async (): Promise<void> => {
|
||||
const checkBatteryOptimization = async (): Promise<void> => {
|
||||
if (Platform.OS === 'android') {
|
||||
try {
|
||||
const canSchedule = await NativeVaultManager.canScheduleExactAlarms();
|
||||
setCanScheduleExactAlarms(canSchedule);
|
||||
const isIgnoring = await NativeVaultManager.isIgnoringBatteryOptimizations();
|
||||
setIsIgnoringBatteryOptimizations(isIgnoring);
|
||||
} catch (error) {
|
||||
console.error('Error checking exact alarm permission:', error);
|
||||
console.error('Error checking battery optimization status:', error);
|
||||
// Default to true to avoid showing the help section unnecessarily
|
||||
setCanScheduleExactAlarms(true);
|
||||
setIsIgnoringBatteryOptimizations(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadCurrentTimeout();
|
||||
checkExactAlarmPermission();
|
||||
checkBatteryOptimization();
|
||||
|
||||
// Listen for app state changes to re-check permission when app comes to foreground
|
||||
const subscription = AppState.addEventListener('change', async (nextAppState) => {
|
||||
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
|
||||
// App coming to foreground, re-check exact alarm permission
|
||||
// App coming to foreground, re-check battery optimization
|
||||
if (Platform.OS === 'android') {
|
||||
await checkExactAlarmPermission();
|
||||
await checkBatteryOptimization();
|
||||
}
|
||||
}
|
||||
appState.current = nextAppState;
|
||||
@@ -83,17 +83,17 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle exact alarm permission request.
|
||||
* Handle battery optimization exemption request.
|
||||
*/
|
||||
const handleRequestExactAlarmPermission = async (): Promise<void> => {
|
||||
const handleRequestBatteryOptimizationExemption = async (): Promise<void> => {
|
||||
if (Platform.OS !== 'android') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await NativeVaultManager.requestExactAlarmPermission();
|
||||
await NativeVaultManager.requestIgnoreBatteryOptimizations();
|
||||
} catch (error) {
|
||||
console.error('Error handling exact alarm permission request:', error);
|
||||
console.error('Error handling battery optimization exemption request:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -167,7 +167,6 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
permissionStatusContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginBottom: 12,
|
||||
},
|
||||
permissionStatusText: {
|
||||
fontSize: 13,
|
||||
@@ -217,36 +216,36 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
{Platform.OS === 'android' && !canScheduleExactAlarms && selectedTimeout > 0 && (
|
||||
{Platform.OS === 'android' && !isIgnoringBatteryOptimizations && selectedTimeout > 0 && (
|
||||
<View style={styles.helpContainer}>
|
||||
<ThemedText style={styles.helpTitle}>
|
||||
{t('settings.exactAlarmHelpTitle')}
|
||||
{t('settings.batteryOptimizationHelpTitle')}
|
||||
</ThemedText>
|
||||
<View style={styles.permissionStatusContainer}>
|
||||
<Ionicons
|
||||
name="alert-circle"
|
||||
name="warning"
|
||||
size={16}
|
||||
color={colors.destructive || '#EF4444'}
|
||||
/>
|
||||
<ThemedText style={[styles.permissionStatusText, styles.permissionDenied]}>
|
||||
{t('settings.exactAlarmPermissionDenied')}
|
||||
{t('settings.batteryOptimizationActive')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={styles.helpDescription}>
|
||||
{t('settings.exactAlarmHelpDescription')}
|
||||
{t('settings.batteryOptimizationHelpDescription')}
|
||||
</ThemedText>
|
||||
<TouchableOpacity
|
||||
style={styles.helpButton}
|
||||
onPress={handleRequestExactAlarmPermission}
|
||||
onPress={handleRequestBatteryOptimizationExemption}
|
||||
>
|
||||
<Ionicons name="settings" size={16} color={colors.background} />
|
||||
<Ionicons name="battery-charging" size={16} color={colors.background} />
|
||||
<ThemedText style={styles.helpButtonText}>
|
||||
{t('settings.enableExactAlarms')}
|
||||
{t('settings.disableBatteryOptimization')}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{Platform.OS === 'android' && canScheduleExactAlarms && selectedTimeout > 0 && (
|
||||
{Platform.OS === 'android' && isIgnoringBatteryOptimizations && selectedTimeout > 0 && (
|
||||
<View style={styles.helpContainer}>
|
||||
<View style={styles.permissionStatusContainer}>
|
||||
<Ionicons
|
||||
@@ -255,12 +254,9 @@ export default function ClipboardClearScreen(): React.ReactNode {
|
||||
color={colors.success || '#10B981'}
|
||||
/>
|
||||
<ThemedText style={[styles.permissionStatusText, styles.permissionGranted]}>
|
||||
{t('settings.exactAlarmPermissionGranted')}
|
||||
{t('settings.batteryOptimizationDisabled')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={styles.helpDescription}>
|
||||
{t('settings.exactAlarmEnabledDescription')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</ThemedScrollView>
|
||||
|
||||
@@ -213,12 +213,11 @@
|
||||
"15seconds": "15 seconds",
|
||||
"30seconds": "30 seconds"
|
||||
},
|
||||
"exactAlarmHelpTitle": "Improve Clipboard Clearing Accuracy",
|
||||
"exactAlarmPermissionDenied": "Missing permission",
|
||||
"exactAlarmPermissionGranted": "Permission granted",
|
||||
"exactAlarmHelpDescription": "Some Android devices require special permission for precise background clipboard clearing. Without this permission, AliasVault uses approximate timing which may be less reliable when the app is in the background.",
|
||||
"exactAlarmEnabledDescription": "AliasVault can clear your clipboard with precise timing, even when the app is in the background.",
|
||||
"enableExactAlarms": "Open app settings",
|
||||
"batteryOptimizationHelpTitle": "Enable Background Clipboard Clearing",
|
||||
"batteryOptimizationActive": "Battery optimization is blocking background tasks",
|
||||
"batteryOptimizationDisabled": "Background clipboard clearing enabled",
|
||||
"batteryOptimizationHelpDescription": "Android's battery optimization prevents reliable clipboard clearing when the app is in the background. Disabling battery optimization for AliasVault allows precise background clipboard clearing and automatically grants necessary alarm permissions.",
|
||||
"disableBatteryOptimization": "Disable battery optimization",
|
||||
"identityGenerator": "Identity Generator",
|
||||
"security": "Security",
|
||||
"appVersion": "App version {{version}} ({{url}})",
|
||||
|
||||
@@ -45,6 +45,10 @@ export interface Spec extends TurboModule {
|
||||
// Exact alarm permission management
|
||||
canScheduleExactAlarms(): Promise<boolean>;
|
||||
requestExactAlarmPermission(): Promise<string>;
|
||||
|
||||
// Battery optimization management
|
||||
isIgnoringBatteryOptimizations(): Promise<boolean>;
|
||||
requestIgnoreBatteryOptimizations(): Promise<string>;
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('NativeVaultManager');
|
||||
|
||||
Reference in New Issue
Block a user