• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, 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 static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_MODE;
20 
21 import android.content.Intent;
22 import android.hardware.display.ColorDisplayManager;
23 import android.hardware.display.NightDisplayListener;
24 import android.metrics.LogMaker;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.provider.Settings;
28 import android.service.quicksettings.Tile;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.View;
32 import android.widget.Switch;
33 
34 import androidx.annotation.Nullable;
35 import androidx.annotation.StringRes;
36 
37 import com.android.internal.logging.MetricsLogger;
38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
39 import com.android.systemui.R;
40 import com.android.systemui.dagger.NightDisplayListenerModule;
41 import com.android.systemui.dagger.qualifiers.Background;
42 import com.android.systemui.dagger.qualifiers.Main;
43 import com.android.systemui.plugins.ActivityStarter;
44 import com.android.systemui.plugins.FalsingManager;
45 import com.android.systemui.plugins.qs.QSTile.BooleanState;
46 import com.android.systemui.plugins.statusbar.StatusBarStateController;
47 import com.android.systemui.qs.QSHost;
48 import com.android.systemui.qs.logging.QSLogger;
49 import com.android.systemui.qs.tileimpl.QSTileImpl;
50 import com.android.systemui.statusbar.policy.LocationController;
51 
52 import java.text.DateFormat;
53 import java.time.LocalTime;
54 import java.time.format.DateTimeFormatter;
55 import java.util.Calendar;
56 import java.util.TimeZone;
57 
58 import javax.inject.Inject;
59 
60 /** Quick settings tile: Night display **/
61 public class NightDisplayTile extends QSTileImpl<BooleanState> implements
62         NightDisplayListener.Callback {
63 
64     public static final String TILE_SPEC = "night";
65 
66     /**
67      * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the
68      * nearest hour and add on the AM/PM indicator.
69      */
70     private static final String PATTERN_HOUR = "h a";
71     private static final String PATTERN_HOUR_MINUTE = "h:mm a";
72     private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm";
73 
74     private ColorDisplayManager mManager;
75     private final LocationController mLocationController;
76     private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
77     private NightDisplayListener mListener;
78     private boolean mIsListening;
79 
80     @Inject
NightDisplayTile( QSHost host, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, LocationController locationController, ColorDisplayManager colorDisplayManager, NightDisplayListenerModule.Builder nightDisplayListenerBuilder )81     public NightDisplayTile(
82             QSHost host,
83             @Background Looper backgroundLooper,
84             @Main Handler mainHandler,
85             FalsingManager falsingManager,
86             MetricsLogger metricsLogger,
87             StatusBarStateController statusBarStateController,
88             ActivityStarter activityStarter,
89             QSLogger qsLogger,
90             LocationController locationController,
91             ColorDisplayManager colorDisplayManager,
92             NightDisplayListenerModule.Builder nightDisplayListenerBuilder
93     ) {
94         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
95                 statusBarStateController, activityStarter, qsLogger);
96         mLocationController = locationController;
97         mManager = colorDisplayManager;
98         mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
99         mListener = mNightDisplayListenerBuilder.setUser(host.getUserContext().getUserId()).build();
100     }
101 
102     @Override
isAvailable()103     public boolean isAvailable() {
104         return ColorDisplayManager.isNightDisplayAvailable(mContext);
105     }
106 
107     @Override
newTileState()108     public BooleanState newTileState() {
109         return new BooleanState();
110     }
111 
112     @Override
handleClick(@ullable View view)113     protected void handleClick(@Nullable View view) {
114         // Enroll in forced auto mode if eligible.
115         if ("1".equals(Settings.Global.getString(mContext.getContentResolver(),
116                 Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE))
117                 && mManager.getNightDisplayAutoModeRaw() == -1) {
118             mManager.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
119             Log.i("NightDisplayTile", "Enrolled in forced night display auto mode");
120         }
121 
122         // Change current activation state.
123         final boolean activated = !mState.value;
124         mManager.setNightDisplayActivated(activated);
125     }
126 
127     @Override
handleUserSwitch(int newUserId)128     protected void handleUserSwitch(int newUserId) {
129         // Stop listening to the old controller.
130         if (mIsListening) {
131             mListener.setCallback(null);
132         }
133 
134         mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class);
135 
136         // Make a new controller for the new user.
137         mListener = mNightDisplayListenerBuilder.setUser(newUserId).build();
138         if (mIsListening) {
139             mListener.setCallback(this);
140         }
141 
142         super.handleUserSwitch(newUserId);
143     }
144 
145     @Override
handleUpdateState(BooleanState state, Object arg)146     protected void handleUpdateState(BooleanState state, Object arg) {
147         state.value = mManager.isNightDisplayActivated();
148         state.label = mContext.getString(R.string.quick_settings_night_display_label);
149         state.expandedAccessibilityClassName = Switch.class.getName();
150         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
151         state.icon = ResourceIcon.get(state.value ? R.drawable.qs_nightlight_icon_on
152                 : R.drawable.qs_nightlight_icon_off);
153         state.secondaryLabel = getSecondaryLabel(state.value);
154         state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
155                 ? state.label
156                 : TextUtils.concat(state.label, ", ", state.secondaryLabel);
157     }
158 
159     /**
160      * Returns a String for the secondary label that reflects when the light will be turned on or
161      * off based on the current auto mode and night light activated status.
162      */
163     @Nullable
getSecondaryLabel(boolean isNightLightActivated)164     private String getSecondaryLabel(boolean isNightLightActivated) {
165         switch (mManager.getNightDisplayAutoMode()) {
166             case ColorDisplayManager.AUTO_MODE_TWILIGHT:
167                 if (!mLocationController.isLocationEnabled()) return null;
168                 // Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be
169                 // turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset.
170                 return isNightLightActivated
171                         ? mContext.getString(
172                                 R.string.quick_settings_night_secondary_label_until_sunrise)
173                         : mContext.getString(
174                                 R.string.quick_settings_night_secondary_label_on_at_sunset);
175 
176             case ColorDisplayManager.AUTO_MODE_CUSTOM_TIME:
177                 // User-specified time, approximated to the nearest hour.
178                 final @StringRes int toggleTimeStringRes;
179                 final LocalTime toggleTime;
180                 final DateTimeFormatter toggleTimeFormat;
181 
182                 if (isNightLightActivated) {
183                     toggleTime = mManager.getNightDisplayCustomEndTime();
184                     toggleTimeStringRes = R.string.quick_settings_secondary_label_until;
185                 } else {
186                     toggleTime = mManager.getNightDisplayCustomStartTime();
187                     toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at;
188                 }
189 
190                 // TODO(b/111085930): Move this calendar snippet to a common code location that
191                 // settings lib can also access.
192                 final Calendar c = Calendar.getInstance();
193                 DateFormat nightTileFormat = android.text.format.DateFormat.getTimeFormat(mContext);
194                 nightTileFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
195                 c.setTimeZone(nightTileFormat.getTimeZone());
196                 c.set(Calendar.HOUR_OF_DAY, toggleTime.getHour());
197                 c.set(Calendar.MINUTE, toggleTime.getMinute());
198                 c.set(Calendar.SECOND, 0);
199                 c.set(Calendar.MILLISECOND, 0);
200                 return mContext.getString(toggleTimeStringRes, nightTileFormat.format(c.getTime()));
201 
202             default:
203                 // No secondary label when auto mode is disabled.
204                 return null;
205         }
206     }
207 
208     @Override
getMetricsCategory()209     public int getMetricsCategory() {
210         return MetricsEvent.QS_NIGHT_DISPLAY;
211     }
212 
213     @Override
populate(LogMaker logMaker)214     public LogMaker populate(LogMaker logMaker) {
215         return super.populate(logMaker)
216                 .addTaggedData(FIELD_QS_MODE, mManager.getNightDisplayAutoModeRaw());
217     }
218 
219     @Override
getLongClickIntent()220     public Intent getLongClickIntent() {
221         return new Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS);
222     }
223 
224     @Override
handleSetListening(boolean listening)225     protected void handleSetListening(boolean listening) {
226         super.handleSetListening(listening);
227         mIsListening = listening;
228         if (listening) {
229             mListener.setCallback(this);
230             refreshState();
231         } else {
232             mListener.setCallback(null);
233         }
234     }
235 
236     @Override
getTileLabel()237     public CharSequence getTileLabel() {
238         return mContext.getString(R.string.quick_settings_night_display_label);
239     }
240 
241     @Override
onActivated(boolean activated)242     public void onActivated(boolean activated) {
243         refreshState();
244     }
245 }
246