システムのNight Modeの切り替え(方法:UiModeManager、API<29)

投稿日:  更新日:

システムを対象としたNight Modeの切り替えは、SettingsアプリとUiModeManagerで行う方法がります。

ここでは、UiModeManagerを使った方法を説明します。

※Night Modeの切り替えの全体像は「Dark ThemeとNight Modeの関係」を参照

※環境:Android Studio Jellyfish | 2023.3.1
    Kotlin 1.9.0
    Compose Compiler 1.5.1
    androidx.compose.foundation:foundation 1.5.0

スポンサーリンク

切り替えの変遷

Night Modeは古く(API8)からあるモードです。

API28までは、UiModeManagerを使って、ユーザのプログラムから制御可能なシステムのパラメータでした。

API29からは、「Dark Theme」機能と連動し、ユーザのプログラムから制御不可能なシステムのパラメータになりました。

代わりにSettingsアプリから制御します。

NightModeの切り替え方法の変遷

API≧29でプログラムから切り替えを行いたければ、システムのパラメータに対するアクセスの許可が必要になります。
※アクセスの許可:MODIFY_DAY_NIGHT_MODEパーミッション

しかし、この許可の取得はユーザのプログラムに許されていません。ですので、実際のところ、切り替えは不可能です。

UiModeManagerService#setNightMode( )
        @Override
        public void setNightMode(int mode) {
            // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM.
            int customModeType = mode == MODE_NIGHT_CUSTOM
                    ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE
                    : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
            setNightModeInternal(mode, customModeType);
        }

        private void setNightModeInternal(int mode, int customModeType) {
            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                    != PackageManager.PERMISSION_GRANTED)) {
                Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
                return;
            }
			...
		}
スポンサーリンク

切り替えの方法

アプリケーションフレームワークのUiModeManager経由で行います。

val _uiModeMgr = getSystemService(UI_MODE_SERVICE) as UiModeManager

// YES/NO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_NO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_YES

// CUSTOM(指定は不可能)
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM)	  // API≧30
_uiModeMgr.customNightModeStart = LocalTime.parse("20:00:00") // アクセス許可が必要
_uiModeMgr.customNightModeEnd = LocalTime.parse("06:00:00")	  // アクセス許可が必要

// AUTO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_AUTO
val _uiModeMgr = LocalContext.current.getSystemService(UI_MODE_SERVICE) as UiModeManager

// YES/NO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_NO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_YES

// CUSTOM(指定は不可能)
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM)	  // API≧30
_uiModeMgr.customNightModeStart = LocalTime.parse("20:00:00") // アクセス許可が必要
_uiModeMgr.customNightModeEnd = LocalTime.parse("06:00:00")	  // アクセス許可が必要

// AUTO
_uiModeMgr.setNightMode(UiModeManager.MODE_NIGHT_AUTO
スポンサーリンク

切り替えのタイプ

指定できる切り替えのタイプは3つです。

切り替えのタイプ(UiModeManager.XXX)結果
手動MODE_NIGHT_YESスイッチDark OnNight
MODE_NIGHT_NODark OffDay
自動MODE_NIGHT_AUTO暦に従う日の入時刻Day⇒Night
日の出時刻Night⇒Day
MODE_NIGHT_CUSTOM
※API≧30
指定時刻Start時刻Day⇒Night
End時刻Night⇒Day
※isSystemInDarkTheme()はUiModeManagerに切り替えられた結果を返す
  true:Night Mode、false:Day Mode

MODE_NIGHT_CUSTOMはAPI≧30の場合に使用できます。ですので、指定は不可能です。

スポンサーリンク

タイプ:YES/NO

手動で行われるOn/Offのスイッチ動作をします。

YESの時刻でDay⇒Nightへ、NOの時刻でNight⇒Dayへ遷移します。

タイプ:YesNoのタイムチャート

スポンサーリンク

タイプ:AUTO

現在地の「日の入」/「日の出」時刻に切り替えを行います。

Android端末は常に自身の地図上の位置を把握するように努めています。位置はGPSやネットワーク、電話のアクセスポイントなどから得ます。現在地は最後に確定した地図上の位置です。

現在地の確認は、Google Mapアプリを開き、現在値ボタンを押下すれば分かります。地図の中心が現在地です。

現在地のこよみ

日の入(Sunset)の時刻でDay⇒Nightへ、日の出(Sunrise)の時刻でNight⇒Dayへ遷移します。

スケジュール:SunsetSunriseのタイムチャート

スポンサーリンク

切り替えの反映

切り替えは直ちに反映されます。

Settingsアプリで行った場合と異なるので注意してください。。

切り替えのタイミング

切り替えは構成の変更を伴って行われます。ですので、Activityは再作成されます。

スポンサーリンク

構成の変更

NightとDay Modeの切り替えが行われると、構成の変更が発生し、Activityが再作成されます。これにより、DarkとLight Themeが切り替わります。

Activityの再作成を行いたくなければ、Manifestファイルへ下記のように追記してください。

Activityの再作成は抑制され、onConfigurationChanged()がコールパックされます。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application ...>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.DarkMode"
			android:configChanges="uiMode">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
class MainActivity : ComponentActivity() {
    ...
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)

        val _nightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
        when(_nightMode) {
            Configuration.UI_MODE_NIGHT_NO -> {/* 処理 */}       // Night Mode
            Configuration.UI_MODE_NIGHT_YES -> {/* 処理 */}      // Day Mode
            Configuration.UI_MODE_NIGHT_UNDEFINED -> {/* 処理 */}
            else -> {}
        }
    }
	...
}

Jetpack Composeにおいて、Activityを再作成しない場合でも、LightとDark Themeが切り替わります。

これは、isSystemInDarkTheme()の出力が変わることで、Composable関数(UI要素)の状態が変化し、再Composeが行われるからです。

スポンサーリンク

付録:エミュレータで現在地の設定

エミュレータで現在地の設定を行う場合は、Extended Controlsパネルを使います。

パネルを開きます。

Extended Controlsパネルを開く

地図を目的の場所へ移動させて、ダブルクリックで場所を決定します。

エミュレータで現在地を設定する

“Set Location”の押下で、現在地として登録されます。

スポンサーリンク

関連記事:

「Android Studio Giraffe」の作成するプロジェクトは、Jetpack Composeの利用が推奨されます。 そして、作成されたプロジェクトは、Material Designeに準拠したテーマが指定されます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
スポンサーリンク