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.annotation.Nullable; 22 import android.app.ActivityManager; 23 import android.content.Intent; 24 import android.metrics.LogMaker; 25 import android.provider.Settings; 26 import android.service.quicksettings.Tile; 27 import android.support.annotation.StringRes; 28 import android.util.Log; 29 import android.widget.Switch; 30 31 import com.android.internal.app.ColorDisplayController; 32 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 33 import com.android.systemui.R; 34 import com.android.systemui.qs.QSHost; 35 import com.android.systemui.plugins.qs.QSTile.BooleanState; 36 import com.android.systemui.qs.tileimpl.QSTileImpl; 37 38 import java.time.LocalTime; 39 import java.time.format.DateTimeFormatter; 40 41 public class NightDisplayTile extends QSTileImpl<BooleanState> 42 implements ColorDisplayController.Callback { 43 44 /** 45 * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the 46 * nearest hour and add on the AM/PM indicator. 47 */ 48 private static final String PATTERN_HOUR = "h a"; 49 private static final String PATTERN_HOUR_MINUTE = "h:mm a"; 50 51 52 private ColorDisplayController mController; 53 private boolean mIsListening; 54 55 public NightDisplayTile(QSHost host) { 56 super(host); 57 mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser()); 58 } 59 60 @Override 61 public boolean isAvailable() { 62 return ColorDisplayController.isAvailable(mContext); 63 } 64 65 @Override 66 public BooleanState newTileState() { 67 return new BooleanState(); 68 } 69 70 @Override 71 protected void handleClick() { 72 // Enroll in forced auto mode if eligible. 73 if ("1".equals(Settings.Global.getString(mContext.getContentResolver(), 74 Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE)) 75 && mController.getAutoModeRaw() == -1) { 76 mController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM); 77 Log.i("NightDisplayTile", "Enrolled in forced night display auto mode"); 78 } 79 80 // Change current activation state. 81 final boolean activated = !mState.value; 82 mController.setActivated(activated); 83 } 84 85 @Override 86 protected void handleUserSwitch(int newUserId) { 87 // Stop listening to the old controller. 88 if (mIsListening) { 89 mController.setListener(null); 90 } 91 92 // Make a new controller for the new user. 93 mController = new ColorDisplayController(mContext, newUserId); 94 if (mIsListening) { 95 mController.setListener(this); 96 } 97 98 super.handleUserSwitch(newUserId); 99 } 100 101 @Override 102 protected void handleUpdateState(BooleanState state, Object arg) { 103 state.value = mController.isActivated(); 104 state.label = state.contentDescription = 105 mContext.getString(R.string.quick_settings_night_display_label); 106 state.icon = ResourceIcon.get(R.drawable.ic_qs_night_display_on); 107 state.expandedAccessibilityClassName = Switch.class.getName(); 108 state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; 109 state.secondaryLabel = getSecondaryLabel(state.value); 110 } 111 112 /** 113 * Returns a {@link String} for the secondary label that reflects when the light will be turned 114 * on or off based on the current auto mode and night light activated status. 115 */ 116 @Nullable 117 private String getSecondaryLabel(boolean isNightLightActivated) { 118 switch(mController.getAutoMode()) { 119 case ColorDisplayController.AUTO_MODE_TWILIGHT: 120 // Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be 121 // turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset. 122 return isNightLightActivated 123 ? mContext.getString( 124 R.string.quick_settings_night_secondary_label_until_sunrise) 125 : mContext.getString( 126 R.string.quick_settings_night_secondary_label_on_at_sunset); 127 128 case ColorDisplayController.AUTO_MODE_CUSTOM: 129 // User-specified time, approximated to the nearest hour. 130 final @StringRes int toggleTimeStringRes; 131 final LocalTime toggleTime; 132 final DateTimeFormatter toggleTimeFormat; 133 134 if (isNightLightActivated) { 135 toggleTime = mController.getCustomEndTime(); 136 toggleTimeStringRes = R.string.quick_settings_secondary_label_until; 137 } else { 138 toggleTime = mController.getCustomStartTime(); 139 toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at; 140 } 141 142 // Choose between just showing the hour or also showing the minutes (based on the 143 // user-selected toggle time). This helps reduce how much space the label takes. 144 toggleTimeFormat = DateTimeFormatter.ofPattern( 145 toggleTime.getMinute() == 0 ? PATTERN_HOUR : PATTERN_HOUR_MINUTE); 146 147 return mContext.getString(toggleTimeStringRes, toggleTime.format(toggleTimeFormat)); 148 149 default: 150 // No secondary label when auto mode is disabled. 151 return null; 152 } 153 } 154 155 @Override 156 public int getMetricsCategory() { 157 return MetricsEvent.QS_NIGHT_DISPLAY; 158 } 159 160 @Override 161 public LogMaker populate(LogMaker logMaker) { 162 return super.populate(logMaker).addTaggedData(FIELD_QS_MODE, mController.getAutoModeRaw()); 163 } 164 165 @Override 166 public Intent getLongClickIntent() { 167 return new Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS); 168 } 169 170 @Override 171 protected void handleSetListening(boolean listening) { 172 mIsListening = listening; 173 if (listening) { 174 mController.setListener(this); 175 refreshState(); 176 } else { 177 mController.setListener(null); 178 } 179 } 180 181 @Override 182 public CharSequence getTileLabel() { 183 return mContext.getString(R.string.quick_settings_night_display_label); 184 } 185 186 @Override 187 public void onActivated(boolean activated) { 188 refreshState(); 189 } 190 } 191