1 /* 2 * Copyright (C) 2019 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.systemui.qs.tiles; 18 19 import android.app.UiModeManager; 20 import android.content.Intent; 21 import android.content.res.Configuration; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.provider.Settings; 25 import android.service.quicksettings.Tile; 26 import android.text.TextUtils; 27 import android.view.View; 28 import android.widget.Switch; 29 30 import androidx.annotation.Nullable; 31 32 import com.android.internal.logging.MetricsLogger; 33 import com.android.internal.logging.nano.MetricsProto; 34 import com.android.systemui.R; 35 import com.android.systemui.dagger.qualifiers.Background; 36 import com.android.systemui.dagger.qualifiers.Main; 37 import com.android.systemui.plugins.ActivityStarter; 38 import com.android.systemui.plugins.FalsingManager; 39 import com.android.systemui.plugins.qs.QSTile; 40 import com.android.systemui.plugins.statusbar.StatusBarStateController; 41 import com.android.systemui.qs.QSHost; 42 import com.android.systemui.qs.QsEventLogger; 43 import com.android.systemui.qs.logging.QSLogger; 44 import com.android.systemui.qs.tileimpl.QSTileImpl; 45 import com.android.systemui.statusbar.policy.BatteryController; 46 import com.android.systemui.statusbar.policy.ConfigurationController; 47 import com.android.systemui.statusbar.policy.LocationController; 48 49 import java.time.LocalTime; 50 import java.time.format.DateTimeFormatter; 51 52 import javax.inject.Inject; 53 54 /** 55 * Quick Settings tile for: Night Mode / Dark Theme / Dark Mode. 56 * 57 * The string id of this tile is "dark" because "night" was already 58 * taken by {@link NightDisplayTile}. 59 */ 60 public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements 61 ConfigurationController.ConfigurationListener, 62 BatteryController.BatteryStateChangeCallback { 63 64 public static final String TILE_SPEC = "dark"; 65 66 public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a"); 67 private final UiModeManager mUiModeManager; 68 private final BatteryController mBatteryController; 69 private final LocationController mLocationController; 70 @Inject UiModeNightTile( QSHost host, QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, ConfigurationController configurationController, BatteryController batteryController, LocationController locationController )71 public UiModeNightTile( 72 QSHost host, 73 QsEventLogger uiEventLogger, 74 @Background Looper backgroundLooper, 75 @Main Handler mainHandler, 76 FalsingManager falsingManager, 77 MetricsLogger metricsLogger, 78 StatusBarStateController statusBarStateController, 79 ActivityStarter activityStarter, 80 QSLogger qsLogger, 81 ConfigurationController configurationController, 82 BatteryController batteryController, 83 LocationController locationController 84 ) { 85 super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, 86 statusBarStateController, activityStarter, qsLogger); 87 mBatteryController = batteryController; 88 mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class); 89 mLocationController = locationController; 90 configurationController.observe(getLifecycle(), this); 91 batteryController.observe(getLifecycle(), this); 92 } 93 94 @Override onUiModeChanged()95 public void onUiModeChanged() { 96 refreshState(); 97 } 98 99 @Override onPowerSaveChanged(boolean isPowerSave)100 public void onPowerSaveChanged(boolean isPowerSave) { 101 refreshState(); 102 } 103 104 @Override newTileState()105 public BooleanState newTileState() { 106 return new BooleanState(); 107 } 108 109 @Override handleClick(@ullable View view)110 protected void handleClick(@Nullable View view) { 111 if (getState().state == Tile.STATE_UNAVAILABLE) { 112 return; 113 } 114 boolean newState = !mState.value; 115 mUiModeManager.setNightModeActivated(newState); 116 refreshState(newState); 117 } 118 119 @Override handleUpdateState(BooleanState state, Object arg)120 protected void handleUpdateState(BooleanState state, Object arg) { 121 int uiMode = mUiModeManager.getNightMode(); 122 boolean powerSave = mBatteryController.isPowerSave(); 123 boolean nightMode = (mContext.getResources().getConfiguration().uiMode 124 & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; 125 126 if (powerSave) { 127 state.secondaryLabel = mContext.getResources().getString( 128 R.string.quick_settings_dark_mode_secondary_label_battery_saver); 129 } else if (uiMode == UiModeManager.MODE_NIGHT_AUTO 130 && mLocationController.isLocationEnabled()) { 131 state.secondaryLabel = mContext.getResources().getString(nightMode 132 ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise 133 : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset); 134 } else if (uiMode == UiModeManager.MODE_NIGHT_CUSTOM) { 135 int nightModeCustomType = mUiModeManager.getNightModeCustomType(); 136 if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) { 137 final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat( 138 mContext); 139 final LocalTime time; 140 if (nightMode) { 141 time = mUiModeManager.getCustomNightModeEnd(); 142 } else { 143 time = mUiModeManager.getCustomNightModeStart(); 144 } 145 state.secondaryLabel = mContext.getResources().getString(nightMode 146 ? R.string.quick_settings_dark_mode_secondary_label_until 147 : R.string.quick_settings_dark_mode_secondary_label_on_at, 148 use24HourFormat ? time.toString() : formatter.format(time)); 149 } else if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { 150 state.secondaryLabel = mContext.getResources().getString(nightMode 151 ? R.string.quick_settings_dark_mode_secondary_label_until_bedtime_ends 152 : R.string.quick_settings_dark_mode_secondary_label_on_at_bedtime); 153 } else { 154 state.secondaryLabel = null; 155 } 156 } else { 157 state.secondaryLabel = null; 158 } 159 state.value = nightMode; 160 state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label); 161 state.contentDescription = TextUtils.isEmpty(state.secondaryLabel) 162 ? state.label 163 : TextUtils.concat(state.label, ", ", state.secondaryLabel); 164 if (powerSave) { 165 state.state = Tile.STATE_UNAVAILABLE; 166 } else { 167 state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; 168 } 169 state.icon = ResourceIcon.get(state.state == Tile.STATE_ACTIVE 170 ? R.drawable.qs_light_dark_theme_icon_on 171 : R.drawable.qs_light_dark_theme_icon_off); 172 state.expandedAccessibilityClassName = Switch.class.getName(); 173 } 174 175 @Override getMetricsCategory()176 public int getMetricsCategory() { 177 return MetricsProto.MetricsEvent.QS_UI_MODE_NIGHT; 178 } 179 180 @Override getLongClickIntent()181 public Intent getLongClickIntent() { 182 return new Intent(Settings.ACTION_DARK_THEME_SETTINGS); 183 } 184 185 @Override getTileLabel()186 public CharSequence getTileLabel() { 187 return getState().label; 188 } 189 } 190