From 444acaee5f3567b85f1747d5ddd803557de79823 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sun, 12 Jul 2026 23:53:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E7=9C=81=E9=9B=BB=E5=8A=9B=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E8=A9=A6=E9=A8=93=E7=9A=84=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=81=8B=E3=82=89=E5=A4=96=E3=81=97=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AB=E3=80=8C=E3=83=90=E3=83=83=E3=83=86=E3=83=AA=E3=83=BC?= =?UTF-8?q?=E3=80=8D=E7=94=BB=E9=9D=A2=E3=82=92=E6=96=B0=E8=A8=AD=E3=81=97?= =?UTF-8?q?=E3=81=A6=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- assets/translations/en.json | 1 + assets/translations/ja.json | 1 + src/components/Permitted.tsx | 2 +- ...useStartBackgroundLocationUpdates.test.tsx | 55 +------ .../useStartBackgroundLocationUpdates.ts | 16 +- src/screens/AppSettings.tsx | 9 + src/screens/BatterySettings.test.tsx | 94 +++++++++++ src/screens/BatterySettings.tsx | 155 ++++++++++++++++++ src/screens/ExperimentalSettings.test.tsx | 32 +--- src/screens/ExperimentalSettings.tsx | 31 ---- src/stacks/MainStack.tsx | 6 + src/store/atoms/battery.ts | 15 ++ src/store/atoms/experimental.ts | 14 -- 13 files changed, 292 insertions(+), 139 deletions(-) create mode 100644 src/screens/BatterySettings.test.tsx create mode 100644 src/screens/BatterySettings.tsx create mode 100644 src/store/atoms/battery.ts diff --git a/assets/translations/en.json b/assets/translations/en.json index 724cd4d2d0..d0b693f0cc 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -331,6 +331,7 @@ "telemetryDescription": "Your device's geographic coordinates are sent to our analytics server. The information is used only for analytics.", "etaAssistTitle": "Improve arrival detection", "etaAssistDescription": "When enabled, in sections where GPS accuracy drops such as subways, the app uses the server's estimated arrival times (ETA) to assist arrival detection. GPS remains the source of truth for arrival and current location; ETA is only an aid.", + "batterySettings": "Battery", "powerSavingLocationTitle": "Power-saving location mode", "powerSavingLocationDescription": "When enabled, location accuracy is lowered to a battery-friendly level and iOS pauses tracking automatically while you are stopped, further reducing battery drain and heat on long rides. The lower accuracy may delay or shift station detection and arrival announcements. The relaxed update frequency is already part of the default settings. It also turns on automatically while your device is in low-power mode.", "passStationLabel": "Pass" diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 58d7f61851..507dfc8cca 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -332,6 +332,7 @@ "telemetryDescription": "お使いの端末の地理的座標を解析用サーバに送信します。送信された情報は解析以外に使用されません。", "etaAssistTitle": "到着判定の改善", "etaAssistDescription": "有効にすると、地下鉄などGPSの精度が落ちる区間で、サーバーの到着予測(ETA)を使って到着判定を補助します。到着や現在地はGPSが基準で、ETAはあくまで補助です。", + "batterySettings": "バッテリー", "powerSavingLocationTitle": "省電力測位モード", "powerSavingLocationDescription": "有効にすると、測位精度を電池優先まで下げ、停車中はiOSが測位を自動休止して、長時間の乗車での電池消費と発熱をさらに減らします。精度の低下により駅の判定や到着案内が遅れたりずれたりする場合があります。位置情報の更新頻度の緩和は標準設定に組み込まれています。端末の省電力モード中は自動的に有効になります。", "passStationLabel": "通過" diff --git a/src/components/Permitted.tsx b/src/components/Permitted.tsx index ad316d742e..dcc82fd75b 100644 --- a/src/components/Permitted.tsx +++ b/src/components/Permitted.tsx @@ -445,7 +445,7 @@ const PermittedLayout: React.FC = ({ children }: Props) => { ); // NOTE: powerSavingLocationEnabledAtom はここでは復元しない。effect復元だと // 継続測位がデフォルト精度で一度起動してから再起動されるため、 - // atom定義側(store/atoms/experimental.ts)でMMKVから同期的に初期値を確定している。 + // atom定義側(store/atoms/battery.ts)でMMKVから同期的に初期値を確定している。 if (themePreferenceKey) { setThemePreference(themePreferenceKey as ThemePreference); diff --git a/src/hooks/useStartBackgroundLocationUpdates.test.tsx b/src/hooks/useStartBackgroundLocationUpdates.test.tsx index 56dfd23ebe..e35df4d673 100644 --- a/src/hooks/useStartBackgroundLocationUpdates.test.tsx +++ b/src/hooks/useStartBackgroundLocationUpdates.test.tsx @@ -13,7 +13,6 @@ import { useStartBackgroundLocationUpdates } from './useStartBackgroundLocationU let mockNeedsJobSchedulerBypass = false; let mockSystemLowPowerMode = false; -let mockIsDevApp = false; jest.mock('../constants/native', () => ({ get NEEDS_JOBSCHEDULER_BYPASS() { return mockNeedsJobSchedulerBypass; @@ -23,11 +22,6 @@ jest.mock('../constants/native', () => ({ jest.mock('expo-battery', () => ({ useLowPowerMode: () => mockSystemLowPowerMode, })); -jest.mock('~/utils/isDevApp', () => ({ - get isDevApp() { - return mockIsDevApp; - }, -})); jest.mock('expo-location'); jest.mock('./useLocationPermissionsGranted'); jest.mock('~/store', () => ({ @@ -44,7 +38,7 @@ jest.mock('~/store/atoms/navigation', () => ({ default: {}, autoModeEnabledAtom: { toString: () => 'autoModeEnabledAtom' }, })); -jest.mock('~/store/atoms/experimental', () => ({ +jest.mock('~/store/atoms/battery', () => ({ powerSavingLocationEnabledAtom: { toString: () => 'powerSavingLocationEnabledAtom', }, @@ -92,7 +86,6 @@ describe('useStartBackgroundLocationUpdates', () => { mockAutoModeEnabled = false; mockPowerSavingLocationEnabled = false; mockSystemLowPowerMode = false; - mockIsDevApp = false; mockNeedsJobSchedulerBypass = false; mockStartLocationUpdatesAsync.mockResolvedValue(undefined); mockStopLocationUpdatesAsync.mockResolvedValue(undefined); @@ -391,7 +384,6 @@ describe('useStartBackgroundLocationUpdates', () => { describe('power saving location mode', () => { test('should lower accuracy and allow automatic pauses for background updates when enabled', async () => { mockPowerSavingLocationEnabled = true; - mockIsDevApp = true; mockUseLocationPermissionsGranted.mockReturnValue(true); renderHook(() => useStartBackgroundLocationUpdates()); @@ -429,10 +421,9 @@ describe('useStartBackgroundLocationUpdates', () => { ); }); - test('should enable the power-saving profile in the dev app while the system low-power mode is active', async () => { + test('should enable the power-saving profile while the system low-power mode is active', async () => { mockPowerSavingLocationEnabled = false; mockSystemLowPowerMode = true; - mockIsDevApp = true; mockUseLocationPermissionsGranted.mockReturnValue(true); renderHook(() => useStartBackgroundLocationUpdates()); @@ -449,50 +440,8 @@ describe('useStartBackgroundLocationUpdates', () => { ); }); - test('should keep the default profile in production while the system low-power mode is active', async () => { - mockPowerSavingLocationEnabled = false; - mockSystemLowPowerMode = true; - mockIsDevApp = false; - mockUseLocationPermissionsGranted.mockReturnValue(true); - - renderHook(() => useStartBackgroundLocationUpdates()); - - await new Promise(process.nextTick); - - expect(mockStartLocationUpdatesAsync).toHaveBeenCalledWith( - LOCATION_TASK_NAME, - expect.objectContaining({ - ...LOCATION_TASK_OPTIONS, - accuracy: Location.Accuracy.High, - pausesUpdatesAutomatically: false, - foregroundService: expect.objectContaining({ - killServiceOnDestroy: true, - }), - }) - ); - }); - - test('should ignore the experimental setting outside the dev app', async () => { - mockPowerSavingLocationEnabled = true; - mockIsDevApp = false; - mockUseLocationPermissionsGranted.mockReturnValue(true); - - renderHook(() => useStartBackgroundLocationUpdates()); - - await new Promise(process.nextTick); - - expect(mockStartLocationUpdatesAsync).toHaveBeenCalledWith( - LOCATION_TASK_NAME, - expect.objectContaining({ - accuracy: Location.Accuracy.High, - pausesUpdatesAutomatically: false, - }) - ); - }); - test('should apply the power-saving accuracy to foreground watchPositionAsync when enabled', async () => { mockPowerSavingLocationEnabled = true; - mockIsDevApp = true; mockUseLocationPermissionsGranted.mockReturnValue(false); renderHook(() => useStartBackgroundLocationUpdates()); diff --git a/src/hooks/useStartBackgroundLocationUpdates.ts b/src/hooks/useStartBackgroundLocationUpdates.ts index 90af2ed21f..6b4b6e6e91 100644 --- a/src/hooks/useStartBackgroundLocationUpdates.ts +++ b/src/hooks/useStartBackgroundLocationUpdates.ts @@ -3,11 +3,10 @@ import * as Location from 'expo-location'; import { useAtomValue } from 'jotai'; import { useEffect } from 'react'; import { store } from '~/store'; -import { powerSavingLocationEnabledAtom } from '~/store/atoms/experimental'; +import { powerSavingLocationEnabledAtom } from '~/store/atoms/battery'; import { backgroundLocationTrackingAtom } from '~/store/atoms/location'; import { autoModeEnabledAtom } from '~/store/atoms/navigation'; import { handleTrackingLocation } from '~/utils/handleTrackingLocation'; -import { isDevApp } from '~/utils/isDevApp'; import { LOCATION_START_MAX_RETRIES, LOCATION_START_RETRY_BASE_DELAY_MS, @@ -30,16 +29,15 @@ export const useStartBackgroundLocationUpdates = () => { const bgPermGranted = useLocationPermissionsGranted(); const autoModeEnabled = useAtomValue(autoModeEnabledAtom); const systemLowPowerMode = Battery.useLowPowerMode(); - // 省電力測位モード(実験的機能)。精度をBalancedへ下げ、停車中の測位自動休止 - // (iOSのみ)を許可する。旧プロファイルのHigh精度・更新間隔の緩和は実車検証を - // 経て既定値へ昇格済み(constants/location.ts)。 + // 省電力測位モード。精度をBalancedへ下げ、停車中の測位自動休止(iOSのみ)を + // 許可する。旧プロファイルのHigh精度・更新間隔の緩和は実車検証を経て既定値へ + // 昇格済み(constants/location.ts)。 const powerSavingSettingEnabled = useAtomValue( powerSavingLocationEnabledAtom ); - // 試験用機能のためdevアプリだけで有効化する。手動設定に加えて、 - // 端末の省電力モード中も自動的に同じプロファイルへ切り替える。 - const powerSavingEnabled = - isDevApp && (powerSavingSettingEnabled || systemLowPowerMode); + // 「バッテリー」設定でONにしたときに加え、端末の省電力モード中も自動的に + // 同じプロファイルへ切り替える。 + const powerSavingEnabled = powerSavingSettingEnabled || systemLowPowerMode; // 選択するオブジェクトはモジュール定数なので、effect依存でも参照が安定する。 const watchOptions = powerSavingEnabled ? LOCATION_WATCH_OPTIONS_POWER_SAVING diff --git a/src/screens/AppSettings.tsx b/src/screens/AppSettings.tsx index 822d2357fe..c2a07b0bbc 100644 --- a/src/screens/AppSettings.tsx +++ b/src/screens/AppSettings.tsx @@ -40,6 +40,7 @@ const SETTING_ITEM_ID_MAP = { personalize_tts: 'personalize_tts', personalize_languages: 'personalize_languages', personalize_notifications: 'personalize_notifications', + personalize_battery: 'personalize_battery', personalize_experimental: 'personalize_experimental', personalize_android: 'personalize_android', about_app_licenses: 'about_app_licenses', @@ -110,6 +111,8 @@ const SettingsItem = ({ return 'globe'; case 'personalize_notifications': return 'notifications'; + case 'personalize_battery': + return 'battery-half'; case 'personalize_experimental': return 'flask'; case 'personalize_android': @@ -312,6 +315,12 @@ const AppSettingsScreen: React.FC = () => { color: '#FF3B30', onPress: () => navigation.navigate('NotificationSettings' as never), }, + { + id: SETTING_ITEM_ID_MAP.personalize_battery, + title: translate('batterySettings'), + color: '#00C7BE', + onPress: () => navigation.navigate('BatterySettings' as never), + }, // 試験的機能はカナリアリリース(devアプリ)限定で表示する ...(isDevApp ? [ diff --git a/src/screens/BatterySettings.test.tsx b/src/screens/BatterySettings.test.tsx new file mode 100644 index 0000000000..09cb1031ed --- /dev/null +++ b/src/screens/BatterySettings.test.tsx @@ -0,0 +1,94 @@ +import { fireEvent, render } from '@testing-library/react-native'; +import { createStore, Provider } from 'jotai'; +import { Alert } from 'react-native'; +import { STORAGE_KEYS } from '~/constants'; +import { storage } from '~/lib/storage'; +import { powerSavingLocationEnabledAtom } from '~/store/atoms/battery'; +import BatterySettingsScreen from './BatterySettings'; + +jest.mock('@react-navigation/native', () => ({ + useNavigation: () => ({ + goBack: jest.fn(), + }), +})); + +jest.mock('~/components/FooterTabBar', () => () => null); +jest.mock('~/components/SettingsHeader', () => ({ + SettingsHeader: () => null, +})); +jest.mock('~/components/Button', () => () => null); +jest.mock('~/translation', () => ({ + translate: (key: string) => key, +})); + +const renderWithStore = (powerSavingLocationEnabled = false) => { + const store = createStore(); + store.set(powerSavingLocationEnabledAtom, powerSavingLocationEnabled); + + const screen = render( + + + + ); + + return { ...screen, store }; +}; + +describe('BatterySettingsScreen', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('省電力測位モードをONにするとatomとストレージへ保存される', () => { + const { getByLabelText, store } = renderWithStore(false); + + fireEvent.press(getByLabelText('powerSavingLocationTitle')); + + expect(store.get(powerSavingLocationEnabledAtom)).toBe(true); + expect(storage.getString(STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED)).toBe( + 'true' + ); + }); + + it('省電力測位モードをOFFにするとatomとストレージへ保存される', () => { + const { getByLabelText, store } = renderWithStore(true); + + fireEvent.press(getByLabelText('powerSavingLocationTitle')); + + expect(store.get(powerSavingLocationEnabledAtom)).toBe(false); + expect(storage.getString(STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED)).toBe( + 'false' + ); + }); + + it('ストレージへの保存に失敗した場合はatom状態をロールバックしエラーを通知する', () => { + const setSpy = jest.spyOn(storage, 'set').mockImplementationOnce(() => { + throw new Error('storage failure'); + }); + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + const alertSpy = jest.spyOn(Alert, 'alert').mockImplementation(() => {}); + + const { getByLabelText, store } = renderWithStore(false); + + fireEvent.press(getByLabelText('powerSavingLocationTitle')); + + // 保存失敗後にロールバックされる(MMKVは同期APIのため即時) + expect(store.get(powerSavingLocationEnabledAtom)).toBe(false); + + // エラーログとユーザーへのアラート表示を検証 + expect(consoleErrorSpy).toHaveBeenCalledWith( + 'Failed to save power saving location setting', + expect.any(Error) + ); + expect(alertSpy).toHaveBeenCalledWith( + 'errorTitle', + 'failedToSavePreference' + ); + + setSpy.mockRestore(); + consoleErrorSpy.mockRestore(); + alertSpy.mockRestore(); + }); +}); diff --git a/src/screens/BatterySettings.tsx b/src/screens/BatterySettings.tsx new file mode 100644 index 0000000000..b02bb8c1a3 --- /dev/null +++ b/src/screens/BatterySettings.tsx @@ -0,0 +1,155 @@ +import { useNavigation } from '@react-navigation/native'; +import { useAtom, useAtomValue } from 'jotai'; +import React, { useCallback, useRef, useState } from 'react'; +import { + Alert, + type NativeScrollEvent, + type NativeSyntheticEvent, + Pressable, + Animated as RNAnimated, + ScrollView, + StyleSheet, + View, +} from 'react-native'; +import Button from '~/components/Button'; +import FooterTabBar from '~/components/FooterTabBar'; +import { SettingsHeader } from '~/components/SettingsHeader'; +import { StatePanel } from '~/components/ToggleButton'; +import Typography from '~/components/Typography'; +import { powerSavingLocationEnabledAtom } from '~/store/atoms/battery'; +import { isLEDThemeAtom } from '~/store/atoms/theme'; +import { translate } from '~/translation'; +import { STORAGE_KEYS } from '../constants'; +import { storage } from '../lib/storage'; + +const styles = StyleSheet.create({ + root: { + paddingHorizontal: 24, + flex: 1, + }, + screenBg: { + backgroundColor: '#FAFAFA', + }, + description: { + marginTop: 16, + color: '#8B8B8B', + lineHeight: 21, + }, + okButton: { + width: 128, + alignSelf: 'center', + marginTop: 32, + }, +}); + +const ToggleItem = ({ + title, + state, + onToggle, +}: { + title: string; + state: boolean; + onToggle: () => void; +}) => { + const isLEDTheme = useAtomValue(isLEDThemeAtom); + + return ( + + + {title} + + + + + ); +}; + +const BatterySettingsScreen: React.FC = () => { + const [headerHeight, setHeaderHeight] = useState(0); + + const scrollY = useRef(new RNAnimated.Value(0)).current; + + const isLEDTheme = useAtomValue(isLEDThemeAtom); + const [powerSavingLocationEnabled, setPowerSavingLocationEnabled] = useAtom( + powerSavingLocationEnabledAtom + ); + + const navigation = useNavigation(); + + const handleTogglePowerSavingLocation = useCallback(() => { + const flag = !powerSavingLocationEnabled; + setPowerSavingLocationEnabled(flag); + try { + storage.set( + STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED, + flag ? 'true' : 'false' + ); + } catch (error) { + // 保存に失敗したままだと次回起動時に設定が巻き戻るため、 + // UIと永続値の不整合を防ぐべくatom状態をロールバックする + setPowerSavingLocationEnabled(!flag); + console.error('Failed to save power saving location setting', error); + Alert.alert(translate('errorTitle'), translate('failedToSavePreference')); + } + }, [powerSavingLocationEnabled, setPowerSavingLocationEnabled]); + + const handleScroll = useCallback( + (e: NativeSyntheticEvent) => { + scrollY.setValue(e.nativeEvent.contentOffset.y); + }, + [scrollY] + ); + + return ( + <> + + + + + {translate('powerSavingLocationDescription')} + + + + + setHeaderHeight(e.nativeEvent.layout.height + 32)} + scrollY={scrollY} + /> + + + ); +}; + +export default React.memo(BatterySettingsScreen); diff --git a/src/screens/ExperimentalSettings.test.tsx b/src/screens/ExperimentalSettings.test.tsx index 191494038d..a70d00fabf 100644 --- a/src/screens/ExperimentalSettings.test.tsx +++ b/src/screens/ExperimentalSettings.test.tsx @@ -7,7 +7,6 @@ import { storage } from '~/lib/storage'; import { etaAssistManualEnabledAtom, portraitModeEnabledAtom, - powerSavingLocationEnabledAtom, } from '~/store/atoms/experimental'; import tuningState from '~/store/atoms/tuning'; import ExperimentalSettingsScreen from './ExperimentalSettings'; @@ -30,13 +29,11 @@ jest.mock('~/translation', () => ({ const renderWithStore = ( portraitModeEnabled: boolean, telemetryEnabled = false, - etaAssistManualEnabled = false, - powerSavingLocationEnabled = false + etaAssistManualEnabled = false ) => { const store = createStore(); store.set(portraitModeEnabledAtom, portraitModeEnabled); store.set(etaAssistManualEnabledAtom, etaAssistManualEnabled); - store.set(powerSavingLocationEnabledAtom, powerSavingLocationEnabled); store.set(tuningState, (prev) => ({ ...prev, telemetryEnabled })); const screen = render( @@ -145,33 +142,6 @@ describe('ExperimentalSettingsScreen', () => { ); }); - it('省電力測位モードをONにするとatomとストレージへ保存される', () => { - const { getByLabelText, store } = renderWithStore(false); - - fireEvent.press(getByLabelText('powerSavingLocationTitle')); - - expect(store.get(powerSavingLocationEnabledAtom)).toBe(true); - expect(storage.getString(STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED)).toBe( - 'true' - ); - }); - - it('省電力測位モードをOFFにするとatomとストレージへ保存される', () => { - const { getByLabelText, store } = renderWithStore( - false, - false, - false, - true - ); - - fireEvent.press(getByLabelText('powerSavingLocationTitle')); - - expect(store.get(powerSavingLocationEnabledAtom)).toBe(false); - expect(storage.getString(STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED)).toBe( - 'false' - ); - }); - it('テレメトリをONにするとatomとストレージへ保存される', () => { const { getByLabelText, store } = renderWithStore(false); diff --git a/src/screens/ExperimentalSettings.tsx b/src/screens/ExperimentalSettings.tsx index e2e9ef48db..692e991cde 100644 --- a/src/screens/ExperimentalSettings.tsx +++ b/src/screens/ExperimentalSettings.tsx @@ -20,7 +20,6 @@ import { isEtaAssistRemoteEnabled } from '~/lib/remoteConfig'; import { etaAssistManualEnabledAtom, portraitModeEnabledAtom, - powerSavingLocationEnabledAtom, } from '~/store/atoms/experimental'; import { isLEDThemeAtom } from '~/store/atoms/theme'; import tuningState from '~/store/atoms/tuning'; @@ -108,9 +107,6 @@ const ExperimentalSettingsScreen: React.FC = () => { const [etaAssistManualEnabled, setEtaAssistManualEnabled] = useAtom( etaAssistManualEnabledAtom ); - const [powerSavingLocationEnabled, setPowerSavingLocationEnabled] = useAtom( - powerSavingLocationEnabledAtom - ); const [tuning, setTuning] = useAtom(tuningState); // Remote Config(マスタースイッチ)が許可していない間は、手動トグルを操作不可にする。 @@ -158,23 +154,6 @@ const ExperimentalSettingsScreen: React.FC = () => { etaAssistRemoteEnabled, ]); - const handleTogglePowerSavingLocation = useCallback(() => { - const flag = !powerSavingLocationEnabled; - setPowerSavingLocationEnabled(flag); - try { - storage.set( - STORAGE_KEYS.POWER_SAVING_LOCATION_ENABLED, - flag ? 'true' : 'false' - ); - } catch (error) { - // 保存に失敗したままだと次回起動時に設定が巻き戻るため、 - // UIと永続値の不整合を防ぐべくatom状態をロールバックする - setPowerSavingLocationEnabled(!flag); - console.error('Failed to save power saving location setting', error); - Alert.alert(translate('errorTitle'), translate('failedToSavePreference')); - } - }, [powerSavingLocationEnabled, setPowerSavingLocationEnabled]); - const handleToggleTelemetry = useCallback(() => { const flag = !tuning.telemetryEnabled; setTuning((prev) => ({ ...prev, telemetryEnabled: flag })); @@ -235,16 +214,6 @@ const ExperimentalSettingsScreen: React.FC = () => { {translate('etaAssistDescription')} - - - - - {translate('powerSavingLocationDescription')} - {translate('experimentalSettingsNotice')} diff --git a/src/stacks/MainStack.tsx b/src/stacks/MainStack.tsx index 01d1974b8f..90ff542228 100644 --- a/src/stacks/MainStack.tsx +++ b/src/stacks/MainStack.tsx @@ -5,6 +5,7 @@ import { import { useAtomValue } from 'jotai'; import React, { useMemo } from 'react'; import AndroidSettings from '~/screens/AndroidSettings'; +import BatterySettings from '~/screens/BatterySettings'; import ExperimentalSettings from '~/screens/ExperimentalSettings'; import Licenses from '~/screens/Licenses'; import NotificationSettings from '~/screens/NotificationSettings'; @@ -113,6 +114,11 @@ const MainStack: React.FC = () => { name="AndroidSettings" component={AndroidSettings} /> + Date: Mon, 13 Jul 2026 00:01:17 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E3=83=90=E3=83=83=E3=83=86=E3=83=AA?= =?UTF-8?q?=E3=83=BC=E9=A0=85=E7=9B=AE=E3=81=AE=E3=82=A2=E3=82=A4=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E8=83=8C=E6=99=AF=E8=89=B2=E3=82=92=E8=90=BD=E3=81=A1?= =?UTF-8?q?=E7=9D=80=E3=81=84=E3=81=9F=E3=83=86=E3=82=A3=E3=83=BC=E3=83=AB?= =?UTF-8?q?(#30B0C7)=E3=81=B8=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- src/screens/AppSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/AppSettings.tsx b/src/screens/AppSettings.tsx index c2a07b0bbc..e2112d7109 100644 --- a/src/screens/AppSettings.tsx +++ b/src/screens/AppSettings.tsx @@ -318,7 +318,7 @@ const AppSettingsScreen: React.FC = () => { { id: SETTING_ITEM_ID_MAP.personalize_battery, title: translate('batterySettings'), - color: '#00C7BE', + color: '#30B0C7', onPress: () => navigation.navigate('BatterySettings' as never), }, // 試験的機能はカナリアリリース(devアプリ)限定で表示する