添加是否忽略电池优化检查

This commit is contained in:
lyswhut 2023-10-04 14:56:10 +08:00
parent 68b22dbd3d
commit a6a8053826
12 changed files with 186 additions and 45 deletions

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".MainApplication"

View File

@ -0,0 +1,40 @@
package cn.toside.music.mobile.utils;
import static android.content.Context.POWER_SERVICE;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
public class BatteryOptimizationUtil {
public static boolean isIgnoringBatteryOptimization(Context context, String packageName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
return powerManager.isIgnoringBatteryOptimizations(packageName);
} else {
return true;
}
}
public static boolean requestIgnoreBatteryOptimization(Context context, String packageName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@SuppressLint("BatteryLife") Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
try {
context.startActivity(intent);
return true;
} catch (Exception ignored) {}
try {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
} catch (Exception ignored) {}
return false;
}
return true;
}
}

View File

@ -255,7 +255,7 @@ public class UtilsModule extends ReactContextBaseJavaModule {
// https://blog.51cto.com/u_15298568/3121162
@ReactMethod
public void openNotificationPermissionActivity() {
public void openNotificationPermissionActivity(Promise promise) {
Intent intent = new Intent();
String packageName = reactContext.getApplicationContext().getPackageName();
@ -268,7 +268,12 @@ public class UtilsModule extends ReactContextBaseJavaModule {
intent.putExtra("app_uid", reactContext.getApplicationContext().getApplicationInfo().uid);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
reactContext.startActivity(intent);
try {
reactContext.startActivity(intent);
promise.resolve(true);
} catch (Exception ignore) {
promise.resolve(false);
}
}
@ReactMethod
@ -371,5 +376,15 @@ public class UtilsModule extends ReactContextBaseJavaModule {
params.putInt("height", rect.height());
promise.resolve(params);
}
@ReactMethod
public void isIgnoringBatteryOptimization(Promise promise) {
promise.resolve(BatteryOptimizationUtil.isIgnoringBatteryOptimization(reactContext.getApplicationContext(), reactContext.getPackageName()));
}
@ReactMethod
public void requestIgnoreBatteryOptimization(Promise promise) {
promise.resolve(BatteryOptimizationUtil.requestIgnoreBatteryOptimization(reactContext.getApplicationContext(), reactContext.getPackageName()));
}
}

View File

@ -1,18 +1,3 @@
落雪提前祝大家中秋快乐~🥮😘!
### 优化
- 通过歌曲菜单添加不喜欢歌曲时需要二次确认防止手抖
- 减慢歌词详情页歌词滚动速度
- 更改应用窗口大小获取方式,尝试解决在某些设备上的背景、弹出菜单显示问题
- 优化同步功能错误消息提示,因同步服务版本不匹配导致的连接失败现在将区分提示
### 修复
- 修复横屏状态下的歌词滚动位置计算问题
- 修复切歌时歌词激活行的重置问题
- 修复更新翻译歌词、罗马音歌词设置后需重启应用才生效的问题,现在更新设置后会立即生效
### 其他
- 更新 React native 到 v0.72.5
- 添加是否忽略电池优化检查用于提醒用户添加白名单确保APP后台播放稳定性

View File

@ -56,6 +56,7 @@ export const storageDataPrefix = {
syncHostHistory: '@sync_host_history',
notificationTipEnable: '@notification_tip_enable',
ignoringBatteryOptimizationTipEnable: '@ignoring_battery_optimization_tip_enable',
searchHistoryList: '@search_history_list',
listUpdateInfo: '@list_update_info',

View File

@ -8,6 +8,7 @@ import BackgroundTimer from 'react-native-background-timer'
import playerState from '@/store/player/state'
import settingState from '@/store/setting/state'
import { onScreenStateChange } from '@/utils/nativeModules/utils'
import { AppState } from 'react-native'
const delaySavePlayInfo = throttleBackgroundTimer(() => {
void savePlayInfo({
@ -158,6 +159,10 @@ export default () => {
} else clearUpdateTimeout()
}
// 修复在某些设备上屏幕状态改变事件未触发导致的进度条未更新的问题
AppState.addEventListener('change', (state) => {
if (state == 'active' && !isScreenOn) handleScreenStateChanged('ON')
})
global.app_event.on('play', handlePlay)
global.app_event.on('pause', handlePause)

View File

@ -24,7 +24,7 @@ import { requestMsg } from '@/utils/message'
import { getRandom } from '@/utils/common'
import { filterList } from './utils'
import BackgroundTimer from 'react-native-background-timer'
import { checkNotificationPermission } from '@/utils/tools'
import { checkIgnoringBatteryOptimization, checkNotificationPermission } from '@/utils/tools'
// import { checkMusicFileAvailable } from '@renderer/utils/music'
@ -180,6 +180,7 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
const handlePlay = async() => {
if (!isInitialized()) {
await checkNotificationPermission()
void checkIgnoringBatteryOptimization()
await playerInitial({
volume: settingState.setting['player.volume'],
playRate: settingState.setting['player.playbackRate'],

View File

@ -2,6 +2,7 @@
"add_to": "Add to...",
"agree": "Agree",
"agree_go": "To turn on",
"agree_to": "Go to settings",
"back": "Back",
"back_home": "Back",
"cancel": "Cancel",
@ -39,6 +40,8 @@
"dislike": "Dislike",
"duplicate_list_tip": "You have previously favorited the list [{name}], do you want to update the songs?",
"exit_app_tip": "Are you sure you want to quit the app?",
"ignoring_battery_optimization_check_tip": "LX Music is not on the list of ignored battery optimization, which may cause the problem of being suspended by the system when playing music in the background. Do you want to add LX Music to the whitelist?",
"ignoring_battery_optimization_check_title": "Background running permission setting reminder",
"input_error": "Don't input indiscriminately 😡",
"list_add_btn_title": "Add the song(s) to {name}",
"list_add_tip_exists": "This song already exists in the list, don't click me again~😡",

View File

@ -2,6 +2,7 @@
"add_to": "添加到...",
"agree": "行行行",
"agree_go": "去开启",
"agree_to": "去设置",
"back": "返回",
"back_home": "返回桌面",
"cancel": "取消",
@ -39,6 +40,8 @@
"dislike": "不喜欢",
"duplicate_list_tip": "你之前已收藏过该列表 [{name}],是否需要更新里面的歌曲?",
"exit_app_tip": "确定要退出应用吗?",
"ignoring_battery_optimization_check_tip": "LX Music没有在忽略电池优化的名单中这可能会导致在后台播放音乐时被系统暂停的问题是否将LX Music加入该白名单中",
"ignoring_battery_optimization_check_title": "后台运行权限设置提醒",
"input_error": "不要乱输好吧😡",
"list_add_btn_title": "把该歌曲添加到 {name}",
"list_add_tip_exists": "列表已经存在这首歌啦,不要再点我啦~😡",

View File

@ -5,7 +5,7 @@ import { StyleSheet, View, InteractionManager } from 'react-native'
import SubTitle from '../../components/SubTitle'
import Button from '../../components/Button'
import { toast, resetNotificationPermissionCheck, confirmDialog } from '@/utils/tools'
import { toast, resetNotificationPermissionCheck, confirmDialog, resetIgnoringBatteryOptimizationCheck } from '@/utils/tools'
import { getAppCacheSize, clearAppCache } from '@/utils/nativeModules/cache'
import { sizeFormate } from '@/utils'
import { useI18n } from '@/lang'
@ -39,6 +39,7 @@ export default memo(() => {
clearAppCache(),
clearMusicUrl(),
resetNotificationPermissionCheck(),
resetIgnoringBatteryOptimizationCheck(),
]).then(() => {
toast(t('setting_other_cache_clear_success_tip'))
}).finally(() => {

View File

@ -1,4 +1,4 @@
import { NativeEventEmitter, NativeModules } from 'react-native'
import { AppState, NativeEventEmitter, NativeModules } from 'react-native'
const { UtilsModule } = NativeModules
@ -29,7 +29,20 @@ export const getDeviceName = async(): Promise<string> => {
export const isNotificationsEnabled = UtilsModule.isNotificationsEnabled as () => Promise<boolean>
export const openNotificationPermissionActivity = UtilsModule.openNotificationPermissionActivity as () => Promise<void>
export const requestNotificationPermission = async() => new Promise<boolean>((resolve) => {
let subscription = AppState.addEventListener('change', (state) => {
if (state != 'active') return
subscription.remove()
setTimeout(() => {
void isNotificationsEnabled().then(resolve)
}, 1000)
})
UtilsModule.openNotificationPermissionActivity().then((result: boolean) => {
if (result) return
subscription.remove()
resolve(false)
})
})
export const shareText = async(shareTitle: string, title: string, text: string): Promise<void> => {
UtilsModule.shareText(shareTitle, title, text)
@ -71,3 +84,22 @@ export const onWindowSizeChange = (callback: (size: { width: number, height: num
eventListener.remove()
}
}
export const isIgnoringBatteryOptimization = async(): Promise<boolean> => {
return UtilsModule.isIgnoringBatteryOptimization()
}
export const requestIgnoreBatteryOptimization = async() => new Promise<boolean>((resolve) => {
let subscription = AppState.addEventListener('change', (state) => {
if (state != 'active') return
subscription.remove()
setTimeout(() => {
void isIgnoringBatteryOptimization().then(resolve)
}, 1000)
})
UtilsModule.requestIgnoreBatteryOptimization().then((result: boolean) => {
if (result) return
subscription.remove()
resolve(false)
})
})

View File

@ -4,7 +4,7 @@ import Clipboard from '@react-native-clipboard/clipboard'
import { storageDataPrefix } from '@/config/constant'
import { gzipFile, unGzipFile } from '@/utils/nativeModules/gzip'
import { temporaryDirectoryPath, unlink } from '@/utils/fs'
import { getSystemLocales, isNotificationsEnabled, openNotificationPermissionActivity, readFile, shareText, writeFile } from '@/utils/nativeModules/utils'
import { getSystemLocales, isIgnoringBatteryOptimization, isNotificationsEnabled, requestNotificationPermission, readFile, requestIgnoreBatteryOptimization, shareText, writeFile } from '@/utils/nativeModules/utils'
import musicSdk from '@/utils/musicSdk'
import { getData, removeData, saveData } from '@/plugins/storage'
import BackgroundTimer from 'react-native-background-timer'
@ -202,35 +202,89 @@ export const checkNotificationPermission = async() => {
if (isHide != null) return
const enabled = await isNotificationsEnabled()
if (enabled) return
Alert.alert(
global.i18n.t('notifications_check_title'),
global.i18n.t('notifications_check_tip'),
[
{
text: global.i18n.t('never_show'),
onPress: () => {
void saveData(storageDataPrefix.notificationTipEnable, '1')
toast(global.i18n.t('disagree_tip'))
return new Promise<void>((resolve) => {
Alert.alert(
global.i18n.t('notifications_check_title'),
global.i18n.t('notifications_check_tip'),
[
{
text: global.i18n.t('never_show'),
onPress: () => {
void saveData(storageDataPrefix.notificationTipEnable, '1')
toast(global.i18n.t('disagree_tip'))
resolve()
},
},
},
{
text: global.i18n.t('disagree'),
onPress: () => {
toast(global.i18n.t('disagree_tip'))
{
text: global.i18n.t('disagree'),
onPress: () => {
toast(global.i18n.t('disagree_tip'))
resolve()
},
},
},
{
text: global.i18n.t('agree_go'),
onPress: () => {
void openNotificationPermissionActivity()
{
text: global.i18n.t('agree_go'),
onPress: () => {
requestAnimationFrame(() => {
void requestNotificationPermission().then((result) => {
if (!result) toast(global.i18n.t('disagree_tip'))
resolve()
})
})
},
},
},
],
)
],
)
})
}
export const checkIgnoringBatteryOptimization = async() => {
const isHide = await getData(storageDataPrefix.ignoringBatteryOptimizationTipEnable)
if (isHide != null) return
const enabled = await isIgnoringBatteryOptimization()
if (enabled) return
return new Promise<void>((resolve) => {
Alert.alert(
global.i18n.t('ignoring_battery_optimization_check_title'),
global.i18n.t('ignoring_battery_optimization_check_tip'),
[
{
text: global.i18n.t('never_show'),
onPress: () => {
void saveData(storageDataPrefix.ignoringBatteryOptimizationTipEnable, '1')
toast(global.i18n.t('disagree_tip'))
resolve()
},
},
{
text: global.i18n.t('disagree'),
onPress: () => {
toast(global.i18n.t('disagree_tip'))
resolve()
},
},
{
text: global.i18n.t('agree_to'),
onPress: () => {
requestAnimationFrame(() => {
void requestIgnoreBatteryOptimization().then((result) => {
if (!result) toast(global.i18n.t('disagree_tip'))
resolve()
})
})
},
},
],
)
})
}
export const resetNotificationPermissionCheck = async() => {
return removeData(storageDataPrefix.notificationTipEnable)
}
export const resetIgnoringBatteryOptimizationCheck = async() => {
return removeData(storageDataPrefix.ignoringBatteryOptimizationTipEnable)
}
export const shareMusic = (shareType: LX.ShareType, downloadFileName: LX.AppSetting['download.fileName'], musicInfo: LX.Music.MusicInfo) => {
const name = musicInfo.name