1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.display; 18 19 import static android.car.settings.CarSettings.Global.FORCED_DAY_NIGHT_MODE; 20 21 import android.car.drivingstate.CarUxRestrictions; 22 import android.car.settings.CarSettings; 23 import android.content.Context; 24 import android.database.ContentObserver; 25 import android.graphics.drawable.Drawable; 26 import android.net.Uri; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.provider.Settings; 30 31 import androidx.annotation.VisibleForTesting; 32 33 import com.android.car.settings.Flags; 34 import com.android.car.settings.R; 35 import com.android.car.settings.common.BaseActionItem; 36 import com.android.car.settings.common.FragmentController; 37 import com.android.car.settings.common.MultiActionPreference; 38 import com.android.car.settings.common.PreferenceController; 39 import com.android.car.settings.common.ToggleButtonActionItem; 40 41 /** 42 * PreferenceController for the day/night theme mode. 43 */ 44 public class ThemeTogglePreferenceController extends PreferenceController<MultiActionPreference> { 45 static final int FORCED_SENSOR_MODE = 0; 46 static final int FORCED_DAY_MODE = 1; 47 static final int FORCED_NIGHT_MODE = 2; 48 49 private static final Uri THEME_MODE_URI = Settings.Global.getUriFor( 50 CarSettings.Global.FORCED_DAY_NIGHT_MODE); 51 private final Handler mHandler = new Handler(Looper.getMainLooper()); 52 private final ContentObserver mThemeToggleObserver = new ContentObserver(mHandler) { 53 @Override 54 public void onChange(boolean selfChange) { 55 refreshUi(); 56 } 57 }; 58 ThemeTogglePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)59 public ThemeTogglePreferenceController(Context context, String preferenceKey, 60 FragmentController fragmentController, 61 CarUxRestrictions uxRestrictions) { 62 super(context, preferenceKey, fragmentController, uxRestrictions); 63 } 64 65 @Override onCreateInternal()66 protected void onCreateInternal() { 67 super.onCreateInternal(); 68 getPreference().setSelectable(false); 69 if (getAutoButton() != null) { 70 getAutoButton().setDrawable(getThemeModeIcon(FORCED_SENSOR_MODE)); 71 getAutoButton().setOnClickListener(isChecked -> { 72 if (isChecked) { 73 setForcedDayNightModeSetting(FORCED_SENSOR_MODE); 74 } 75 refreshUi(); 76 }); 77 } 78 if (getDayButton() != null) { 79 getDayButton().setDrawable(getThemeModeIcon(FORCED_DAY_MODE)); 80 getDayButton().setOnClickListener(isChecked -> { 81 if (isChecked) { 82 setForcedDayNightModeSetting(FORCED_DAY_MODE); 83 } 84 refreshUi(); 85 }); 86 } 87 if (getNightButton() != null) { 88 getNightButton().setDrawable(getThemeModeIcon(FORCED_NIGHT_MODE)); 89 getNightButton().setOnClickListener(isChecked -> { 90 if (isChecked) { 91 setForcedDayNightModeSetting(FORCED_NIGHT_MODE); 92 } 93 refreshUi(); 94 }); 95 } 96 } 97 98 @Override onStartInternal()99 protected void onStartInternal() { 100 super.onStartInternal(); 101 getContext().getContentResolver().registerContentObserver(THEME_MODE_URI, 102 /* notifyForDescendants= */ false, mThemeToggleObserver); 103 } 104 105 @Override onStopInternal()106 protected void onStopInternal() { 107 super.onStopInternal(); 108 getContext().getContentResolver().unregisterContentObserver(mThemeToggleObserver); 109 } 110 111 @Override updateState(MultiActionPreference preference)112 protected void updateState(MultiActionPreference preference) { 113 if (getAutoButton() != null) { 114 boolean checked = getForcedDayNightModeSetting() == FORCED_SENSOR_MODE; 115 getAutoButton().setChecked(checked); 116 getAutoButton().setEnabled(!checked); 117 } 118 if (getDayButton() != null) { 119 boolean checked = getForcedDayNightModeSetting() == FORCED_DAY_MODE; 120 getDayButton().setChecked(checked); 121 getDayButton().setEnabled(!checked); 122 } 123 if (getNightButton() != null) { 124 boolean checked = getForcedDayNightModeSetting() == FORCED_NIGHT_MODE; 125 getNightButton().setChecked(checked); 126 getNightButton().setEnabled(!checked); 127 } 128 } 129 130 @Override getPreferenceType()131 protected Class<MultiActionPreference> getPreferenceType() { 132 return MultiActionPreference.class; 133 } 134 135 @Override getDefaultAvailabilityStatus()136 public int getDefaultAvailabilityStatus() { 137 if (!android.car.feature.Flags.carNightGlobalSetting() || !Flags.uiThemeToggle()) { 138 return UNSUPPORTED_ON_DEVICE; 139 } 140 return AVAILABLE; 141 } 142 143 @VisibleForTesting getAutoButton()144 ToggleButtonActionItem getAutoButton() { 145 BaseActionItem item = getPreference().getActionItem( 146 MultiActionPreference.ActionItem.ACTION_ITEM1); 147 if (item instanceof ToggleButtonActionItem) { 148 return (ToggleButtonActionItem) item; 149 } 150 return null; 151 } 152 153 @VisibleForTesting getDayButton()154 ToggleButtonActionItem getDayButton() { 155 BaseActionItem item = getPreference().getActionItem( 156 MultiActionPreference.ActionItem.ACTION_ITEM2); 157 if (item instanceof ToggleButtonActionItem) { 158 return (ToggleButtonActionItem) item; 159 } 160 return null; 161 } 162 163 @VisibleForTesting getNightButton()164 ToggleButtonActionItem getNightButton() { 165 BaseActionItem item = getPreference().getActionItem( 166 MultiActionPreference.ActionItem.ACTION_ITEM3); 167 if (item instanceof ToggleButtonActionItem) { 168 return (ToggleButtonActionItem) item; 169 } 170 return null; 171 } 172 getForcedDayNightModeSetting()173 private int getForcedDayNightModeSetting() { 174 return Settings.Global.getInt(getContext().getContentResolver(), 175 FORCED_DAY_NIGHT_MODE, FORCED_SENSOR_MODE); 176 } 177 setForcedDayNightModeSetting(int mode)178 private void setForcedDayNightModeSetting(int mode) { 179 Settings.Global.putInt(getContext().getContentResolver(), FORCED_DAY_NIGHT_MODE, mode); 180 } 181 getThemeModeIcon(int mode)182 private Drawable getThemeModeIcon(int mode) { 183 return switch (mode) { 184 case FORCED_SENSOR_MODE -> getContext().getDrawable( 185 R.drawable.ic_theme_toggle_auto_button); 186 case FORCED_DAY_MODE -> getContext().getDrawable(R.drawable.ic_theme_toggle_day_button); 187 case FORCED_NIGHT_MODE -> getContext().getDrawable( 188 R.drawable.ic_theme_toggle_night_button); 189 default -> null; 190 }; 191 } 192 } 193