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