blob: 326df498c9847c24992beb706e8269feeb12a7f5 [file] [log] [blame]
Jason Monke5b770e2017-03-03 21:49:29 -05001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.qs;
16
Charles Hece2a7c02017-10-11 20:25:20 +010017import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
Charles Hece2a7c02017-10-11 20:25:20 +010018
Rohan Shahd3cf7562018-02-23 11:12:28 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Amin Shaikh0e003312018-03-08 11:39:01 -050021import android.annotation.ColorInt;
Lucas Dupin1f7374a2018-02-26 18:08:33 -080022import android.app.ActivityManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080023import android.app.AlarmManager;
Amin Shaikh761c5662018-03-27 16:55:43 -040024import android.content.BroadcastReceiver;
Jason Monke5b770e2017-03-03 21:49:29 -050025import android.content.Context;
Evan Laird4ea2a492018-01-22 11:29:12 -050026import android.content.Intent;
Amin Shaikh761c5662018-03-27 16:55:43 -040027import android.content.IntentFilter;
Jason Monke5b770e2017-03-03 21:49:29 -050028import android.content.res.Configuration;
Rohan Shahcc3d1d82018-03-30 21:24:17 +000029import android.content.res.Resources;
Jason Monke5b770e2017-03-03 21:49:29 -050030import android.graphics.Color;
31import android.graphics.Rect;
Amin Shaikh761c5662018-03-27 16:55:43 -040032import android.media.AudioManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080033import android.os.Handler;
Evan Laird4ea2a492018-01-22 11:29:12 -050034import android.provider.AlarmClock;
Beverlyb9826dd2018-04-12 15:02:45 -040035import android.service.notification.ZenModeConfig;
Rohan Shahd3cf7562018-02-23 11:12:28 -080036import android.text.format.DateUtils;
Jason Monke5b770e2017-03-03 21:49:29 -050037import android.util.AttributeSet;
Rohan Shaha160ab12018-03-09 15:47:36 -080038import android.util.Log;
Adrian Roos13836052018-03-15 21:06:37 +010039import android.util.Pair;
Jason Monk824ffff2017-04-11 15:49:06 -040040import android.view.View;
Adrian Roos13836052018-03-15 21:06:37 +010041import android.view.WindowInsets;
Beverlyff2df9b2018-10-10 16:54:10 -040042import android.widget.FrameLayout;
Amin Shaikh761c5662018-03-27 16:55:43 -040043import android.widget.ImageView;
Jason Monke5b770e2017-03-03 21:49:29 -050044import android.widget.RelativeLayout;
Rohan Shahd3cf7562018-02-23 11:12:28 -080045import android.widget.TextView;
Jason Monke5b770e2017-03-03 21:49:29 -050046
Beverlyff2df9b2018-10-10 16:54:10 -040047import androidx.annotation.VisibleForTesting;
48
Jason Monke5b770e2017-03-03 21:49:29 -050049import com.android.settingslib.Utils;
50import com.android.systemui.BatteryMeterView;
51import com.android.systemui.Dependency;
Rohan Shahd3cf7562018-02-23 11:12:28 -080052import com.android.systemui.Prefs;
Jason Monke5b770e2017-03-03 21:49:29 -050053import com.android.systemui.R;
54import com.android.systemui.plugins.ActivityStarter;
55import com.android.systemui.qs.QSDetail.Callback;
Adrian Roos13836052018-03-15 21:06:37 +010056import com.android.systemui.statusbar.phone.PhoneStatusBarView;
Evan Laird95896952018-01-22 19:30:05 -050057import com.android.systemui.statusbar.phone.StatusBarIconController;
58import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
Evan Laird20b87bf2018-04-12 09:54:11 -040059import com.android.systemui.statusbar.phone.StatusIconContainer;
Beverlyff2df9b2018-10-10 16:54:10 -040060import com.android.systemui.statusbar.policy.Clock;
Evan Laird39254d42018-01-18 16:05:30 -050061import com.android.systemui.statusbar.policy.DarkIconDispatcher;
Jason Monk824ffff2017-04-11 15:49:06 -040062import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Rohan Shahcc3d1d82018-03-30 21:24:17 +000063import com.android.systemui.statusbar.policy.DateView;
Rohan Shahd3cf7562018-02-23 11:12:28 -080064import com.android.systemui.statusbar.policy.NextAlarmController;
Beverlyb9826dd2018-04-12 15:02:45 -040065import com.android.systemui.statusbar.policy.ZenModeController;
Jason Monke5b770e2017-03-03 21:49:29 -050066
Lucas Dupin1f7374a2018-02-26 18:08:33 -080067import java.util.Locale;
Beverlyb9826dd2018-04-12 15:02:45 -040068import java.util.Objects;
Lucas Dupin1f7374a2018-02-26 18:08:33 -080069
Rohan Shahd3cf7562018-02-23 11:12:28 -080070/**
71 * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
72 * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
73 * contents.
74 */
Amin Shaikhf09450b2018-04-06 17:32:45 -040075public class QuickStatusBarHeader extends RelativeLayout implements
Beverlyb9826dd2018-04-12 15:02:45 -040076 View.OnClickListener, NextAlarmController.NextAlarmChangeCallback,
77 ZenModeController.Callback {
Rohan Shaha160ab12018-03-09 15:47:36 -080078 private static final String TAG = "QuickStatusBarHeader";
79 private static final boolean DEBUG = false;
Jason Monke5b770e2017-03-03 21:49:29 -050080
Rohan Shahd3cf7562018-02-23 11:12:28 -080081 /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
82 private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
83 private static final int FADE_ANIMATION_DURATION_MS = 300;
84 private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
Rohan Shahb6a915c2018-03-01 11:57:00 -080085 public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
Rohan Shahd3cf7562018-02-23 11:12:28 -080086
87 private final Handler mHandler = new Handler();
Jason Monke5b770e2017-03-03 21:49:29 -050088
89 private QSPanel mQsPanel;
90
91 private boolean mExpanded;
92 private boolean mListening;
Charles Hece2a7c02017-10-11 20:25:20 +010093 private boolean mQsDisabled;
Jason Monke5b770e2017-03-03 21:49:29 -050094
95 protected QuickQSPanel mHeaderQsPanel;
96 protected QSTileHost mHost;
Evan Laird95896952018-01-22 19:30:05 -050097 private TintedIconManager mIconManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080098 private TouchAnimator mStatusIconsAlphaAnimator;
99 private TouchAnimator mHeaderTextContainerAlphaAnimator;
Evan Laird95896952018-01-22 19:30:05 -0500100
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000101 private View mSystemIconsView;
Evan Laird95896952018-01-22 19:30:05 -0500102 private View mQuickQsStatusIcons;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800103 private View mHeaderTextContainerView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400104 /** View containing the next alarm and ringer mode info. */
105 private View mStatusContainer;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800106 /** Tooltip for educating users that they can long press on icons to see more details. */
107 private View mLongPressTooltipView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400108
109 private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
110 private AlarmManager.AlarmClockInfo mNextAlarm;
111
112 private ImageView mNextAlarmIcon;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800113 /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
114 private TextView mNextAlarmTextView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400115 private View mStatusSeparator;
116 private ImageView mRingerModeIcon;
117 private TextView mRingerModeTextView;
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000118 private BatteryMeterView mBatteryMeterView;
119 private Clock mClockView;
120 private DateView mDateView;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800121
122 private NextAlarmController mAlarmController;
Beverlyb9826dd2018-04-12 15:02:45 -0400123 private ZenModeController mZenController;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800124 /** Counts how many times the long press tooltip has been shown to the user. */
125 private int mShownCount;
126
Amin Shaikh761c5662018-03-27 16:55:43 -0400127 private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
128 @Override
129 public void onReceive(Context context, Intent intent) {
130 mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
131 updateStatusText();
132 }
133 };
134
Rohan Shahd3cf7562018-02-23 11:12:28 -0800135 /**
136 * Runnable for automatically fading out the long press tooltip (as if it were animating away).
137 */
138 private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
Evan Laird4ea2a492018-01-22 11:29:12 -0500139
Jason Monke5b770e2017-03-03 21:49:29 -0500140 public QuickStatusBarHeader(Context context, AttributeSet attrs) {
141 super(context, attrs);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800142 mAlarmController = Dependency.get(NextAlarmController.class);
Beverlyb9826dd2018-04-12 15:02:45 -0400143 mZenController = Dependency.get(ZenModeController.class);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800144 mShownCount = getStoredShownCount();
Jason Monke5b770e2017-03-03 21:49:29 -0500145 }
146
147 @Override
148 protected void onFinishInflate() {
149 super.onFinishInflate();
Jason Monke5b770e2017-03-03 21:49:29 -0500150
151 mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000152 mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
Evan Laird95896952018-01-22 19:30:05 -0500153 mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
Evan Laird20b87bf2018-04-12 09:54:11 -0400154 StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
155 iconContainer.setShouldRestrictIcons(false);
156 mIconManager = new TintedIconManager(iconContainer);
Jason Monke5b770e2017-03-03 21:49:29 -0500157
Rohan Shahd3cf7562018-02-23 11:12:28 -0800158 // Views corresponding to the header info section (e.g. tooltip and next alarm).
159 mHeaderTextContainerView = findViewById(R.id.header_text_container);
160 mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
Amin Shaikh761c5662018-03-27 16:55:43 -0400161 mStatusContainer = findViewById(R.id.status_container);
162 mStatusSeparator = findViewById(R.id.status_separator);
163 mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800164 mNextAlarmTextView = findViewById(R.id.next_alarm_text);
Amin Shaikh761c5662018-03-27 16:55:43 -0400165 mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
166 mRingerModeTextView = findViewById(R.id.ringer_mode_text);
Jason Monke5b770e2017-03-03 21:49:29 -0500167
168 updateResources();
169
Jason Monk824ffff2017-04-11 15:49:06 -0400170 Rect tintArea = new Rect(0, 0, 0, 0);
Jason Changb4e879d2018-04-11 11:17:58 +0800171 int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
172 android.R.attr.colorForeground);
Amin Shaikh0e003312018-03-08 11:39:01 -0500173 float intensity = getColorIntensity(colorForeground);
Evan Laird95896952018-01-22 19:30:05 -0500174 int fillColor = fillColorForIntensity(intensity, getContext());
175
176 // Set light text on the header icons because they will always be on a black background
Evan Laird39254d42018-01-18 16:05:30 -0500177 applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Evan Laird95896952018-01-22 19:30:05 -0500178
179 // Set the correct tint for the status icons so they contrast
180 mIconManager.setTint(fillColor);
Jason Monke5b770e2017-03-03 21:49:29 -0500181
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000182 mBatteryMeterView = findViewById(R.id.battery);
183 mBatteryMeterView.setForceShowPercent(true);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400184 mBatteryMeterView.setOnClickListener(this);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000185 mClockView = findViewById(R.id.clock);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400186 mClockView.setOnClickListener(this);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000187 mDateView = findViewById(R.id.date);
Jason Monke5b770e2017-03-03 21:49:29 -0500188 }
189
Amin Shaikh761c5662018-03-27 16:55:43 -0400190 private void updateStatusText() {
Beverlyb9826dd2018-04-12 15:02:45 -0400191 boolean changed = updateRingerStatus() || updateAlarmStatus();
192
193 if (changed) {
194 boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
195 boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
196 mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
197 : View.GONE);
198 updateTooltipShow();
199 }
200 }
201
202 private boolean updateRingerStatus() {
203 boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
204 CharSequence originalRingerText = mRingerModeTextView.getText();
205
Amin Shaikh761c5662018-03-27 16:55:43 -0400206 boolean ringerVisible = false;
Beverlyb9826dd2018-04-12 15:02:45 -0400207 if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(),
Beverlyff2df9b2018-10-10 16:54:10 -0400208 mZenController.getConsolidatedPolicy())) {
Beverlyb9826dd2018-04-12 15:02:45 -0400209 if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
210 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_vibrate);
211 mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
212 ringerVisible = true;
213 } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) {
214 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_silent);
215 mRingerModeTextView.setText(R.string.qs_status_phone_muted);
216 ringerVisible = true;
217 }
Amin Shaikh761c5662018-03-27 16:55:43 -0400218 }
219 mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
220 mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
221
Beverlyb9826dd2018-04-12 15:02:45 -0400222 return isOriginalVisible != ringerVisible ||
223 !Objects.equals(originalRingerText, mRingerModeTextView.getText());
224 }
225
226 private boolean updateAlarmStatus() {
227 boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
228 CharSequence originalAlarmText = mNextAlarmTextView.getText();
229
Amin Shaikh761c5662018-03-27 16:55:43 -0400230 boolean alarmVisible = false;
231 if (mNextAlarm != null) {
232 alarmVisible = true;
233 mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm));
234 }
235 mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
236 mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
Amin Shaikh761c5662018-03-27 16:55:43 -0400237
Beverlyb9826dd2018-04-12 15:02:45 -0400238 return isOriginalVisible != alarmVisible ||
239 !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
240 }
Amin Shaikh761c5662018-03-27 16:55:43 -0400241
Jason Monk824ffff2017-04-11 15:49:06 -0400242 private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
243 View v = findViewById(id);
244 if (v instanceof DarkReceiver) {
245 ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
246 }
247 }
248
Evan Laird95896952018-01-22 19:30:05 -0500249 private int fillColorForIntensity(float intensity, Context context) {
250 if (intensity == 0) {
Amin Shaikh9cac3382018-04-11 17:10:07 -0400251 return context.getColor(R.color.light_mode_icon_color_single_tone);
Evan Laird95896952018-01-22 19:30:05 -0500252 }
Amin Shaikh9cac3382018-04-11 17:10:07 -0400253 return context.getColor(R.color.dark_mode_icon_color_single_tone);
Evan Laird95896952018-01-22 19:30:05 -0500254 }
255
Jason Monke5b770e2017-03-03 21:49:29 -0500256 @Override
257 protected void onConfigurationChanged(Configuration newConfig) {
258 super.onConfigurationChanged(newConfig);
259 updateResources();
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000260
261 // Update color schemes in landscape to use wallpaperTextColor
262 boolean shouldUseWallpaperTextColor =
263 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
264 mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
265 mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
Jason Monke5b770e2017-03-03 21:49:29 -0500266 }
267
268 @Override
269 public void onRtlPropertiesChanged(int layoutDirection) {
270 super.onRtlPropertiesChanged(layoutDirection);
271 updateResources();
272 }
273
Evan Laird84b98952018-07-02 17:43:59 -0400274 /**
275 * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
276 * when there is a notch involved the status bar can remain a fixed pixel size.
277 */
278 private void updateMinimumHeight() {
279 int sbHeight = mContext.getResources().getDimensionPixelSize(
280 com.android.internal.R.dimen.status_bar_height);
281 int qqsHeight = mContext.getResources().getDimensionPixelSize(
282 R.dimen.qs_quick_header_panel_height);
283
284 setMinimumHeight(sbHeight + qqsHeight);
285 }
286
Jason Monke5b770e2017-03-03 21:49:29 -0500287 private void updateResources() {
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000288 Resources resources = mContext.getResources();
Evan Laird84b98952018-07-02 17:43:59 -0400289 updateMinimumHeight();
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000290
291 // Update height for a few views, especially due to landscape mode restricting space.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800292 mHeaderTextContainerView.getLayoutParams().height =
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000293 resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800294 mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
295
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000296 mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
297 com.android.internal.R.dimen.quick_qs_offset_height);
298 mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
299
Evan Laird84b98952018-07-02 17:43:59 -0400300 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
301 if (mQsDisabled) {
302 lp.height = resources.getDimensionPixelSize(
303 com.android.internal.R.dimen.quick_qs_offset_height);
304 } else {
305 lp.height = Math.max(getMinimumHeight(),
306 resources.getDimensionPixelSize(
Evan Lairdbf3794a2018-07-17 15:55:40 -0400307 com.android.internal.R.dimen.quick_qs_total_height));
Evan Laird84b98952018-07-02 17:43:59 -0400308 }
309
310 setLayoutParams(lp);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000311
Rohan Shahd3cf7562018-02-23 11:12:28 -0800312 updateStatusIconAlphaAnimator();
313 updateHeaderTextContainerAlphaAnimator();
Evan Laird95896952018-01-22 19:30:05 -0500314 }
315
Rohan Shahd3cf7562018-02-23 11:12:28 -0800316 private void updateStatusIconAlphaAnimator() {
317 mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
Evan Laird95896952018-01-22 19:30:05 -0500318 .addFloat(mQuickQsStatusIcons, "alpha", 1, 0)
319 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500320 }
321
Rohan Shahd3cf7562018-02-23 11:12:28 -0800322 private void updateHeaderTextContainerAlphaAnimator() {
323 mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
324 .addFloat(mHeaderTextContainerView, "alpha", 0, 1)
325 .setStartDelay(.5f)
326 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500327 }
328
329 public void setExpanded(boolean expanded) {
330 if (mExpanded == expanded) return;
331 mExpanded = expanded;
332 mHeaderQsPanel.setExpanded(expanded);
333 updateEverything();
334 }
335
Rohan Shahd3cf7562018-02-23 11:12:28 -0800336 /**
337 * Animates the inner contents based on the given expansion details.
338 *
339 * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen)
340 * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
341 * @param panelTranslationY how much the panel has physically moved down vertically (required
342 * for keyguard animations only)
343 */
344 public void setExpansion(boolean isKeyguardShowing, float expansionFraction,
345 float panelTranslationY) {
346 final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction;
347 if (mStatusIconsAlphaAnimator != null) {
348 mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
Evan Laird95896952018-01-22 19:30:05 -0500349 }
Rohan Shahd3cf7562018-02-23 11:12:28 -0800350
351 if (isKeyguardShowing) {
352 // If the keyguard is showing, we want to offset the text so that it comes in at the
353 // same time as the panel as it slides down.
354 mHeaderTextContainerView.setTranslationY(panelTranslationY);
355 } else {
356 mHeaderTextContainerView.setTranslationY(0f);
357 }
358
359 if (mHeaderTextContainerAlphaAnimator != null) {
360 mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
361 }
362
363 // Check the original expansion fraction - we don't want to show the tooltip until the
364 // panel is pulled all the way out.
365 if (expansionFraction == 1f) {
366 // QS is fully expanded, bring in the tooltip.
367 showLongPressTooltip();
368 }
369 }
370
371 /** Returns the latest stored tooltip shown count from SharedPreferences. */
372 private int getStoredShownCount() {
373 return Prefs.getInt(
374 mContext,
375 Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
376 TOOLTIP_NOT_YET_SHOWN_COUNT);
Jason Monke5b770e2017-03-03 21:49:29 -0500377 }
378
Charles Hece2a7c02017-10-11 20:25:20 +0100379 public void disable(int state1, int state2, boolean animate) {
380 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
381 if (disabled == mQsDisabled) return;
382 mQsDisabled = disabled;
383 mHeaderQsPanel.setDisabledByPolicy(disabled);
Amin Shaikhf09450b2018-04-06 17:32:45 -0400384 mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
385 mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
386 updateResources();
Charles Hece2a7c02017-10-11 20:25:20 +0100387 }
388
389 @Override
390 public void onAttachedToWindow() {
Amin Shaikha59215a2018-04-23 08:52:38 -0400391 super.onAttachedToWindow();
Evan Laird95896952018-01-22 19:30:05 -0500392 Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
Adrian Roos13836052018-03-15 21:06:37 +0100393 requestApplyInsets();
394 }
395
396 @Override
397 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
398 Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
399 insets.getDisplayCutout(), getDisplay());
400 if (padding == null) {
Amin Shaikh7d86b042018-04-16 16:00:12 -0400401 mSystemIconsView.setPaddingRelative(
402 getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
403 getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
Adrian Roos13836052018-03-15 21:06:37 +0100404 } else {
Amin Shaikh7d86b042018-04-16 16:00:12 -0400405 mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
406
Adrian Roos13836052018-03-15 21:06:37 +0100407 }
408 return super.onApplyWindowInsets(insets);
Charles Hece2a7c02017-10-11 20:25:20 +0100409 }
410
411 @Override
Jason Monke5b770e2017-03-03 21:49:29 -0500412 @VisibleForTesting
413 public void onDetachedFromWindow() {
414 setListening(false);
Evan Laird95896952018-01-22 19:30:05 -0500415 Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
Jason Monke5b770e2017-03-03 21:49:29 -0500416 super.onDetachedFromWindow();
417 }
418
419 public void setListening(boolean listening) {
420 if (listening == mListening) {
421 return;
422 }
423 mHeaderQsPanel.setListening(listening);
424 mListening = listening;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800425
426 if (listening) {
Beverlyb9826dd2018-04-12 15:02:45 -0400427 mZenController.addCallback(this);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800428 mAlarmController.addCallback(this);
Amin Shaikh761c5662018-03-27 16:55:43 -0400429 mContext.registerReceiver(mRingerReceiver,
430 new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
Rohan Shahd3cf7562018-02-23 11:12:28 -0800431 } else {
Beverlyb9826dd2018-04-12 15:02:45 -0400432 mZenController.removeCallback(this);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800433 mAlarmController.removeCallback(this);
Amin Shaikh761c5662018-03-27 16:55:43 -0400434 mContext.unregisterReceiver(mRingerReceiver);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800435 }
Jason Monke5b770e2017-03-03 21:49:29 -0500436 }
437
Evan Laird95896952018-01-22 19:30:05 -0500438 @Override
439 public void onClick(View v) {
Amin Shaikh39bdc502018-05-15 23:43:34 -0400440 if (v == mClockView) {
Evan Laird95896952018-01-22 19:30:05 -0500441 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
442 AlarmClock.ACTION_SHOW_ALARMS),0);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400443 } else if (v == mBatteryMeterView) {
444 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
445 Intent.ACTION_POWER_USAGE_SUMMARY),0);
Evan Laird95896952018-01-22 19:30:05 -0500446 }
447 }
448
Rohan Shahd3cf7562018-02-23 11:12:28 -0800449 @Override
450 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
Amin Shaikh761c5662018-03-27 16:55:43 -0400451 mNextAlarm = nextAlarm;
452 updateStatusText();
453 }
Rohan Shaha160ab12018-03-09 15:47:36 -0800454
Beverlyb9826dd2018-04-12 15:02:45 -0400455 @Override
456 public void onZenChanged(int zen) {
457 updateStatusText();
458
459 }
460
461 @Override
462 public void onConfigChanged(ZenModeConfig config) {
463 updateStatusText();
464 }
465
Amin Shaikh761c5662018-03-27 16:55:43 -0400466 private void updateTooltipShow() {
467 if (hasStatusText()) {
468 hideLongPressTooltip(true /* shouldShowStatusText */);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800469 } else {
Amin Shaikh761c5662018-03-27 16:55:43 -0400470 hideStatusText();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800471 }
472 updateHeaderTextContainerAlphaAnimator();
473 }
474
Amin Shaikh761c5662018-03-27 16:55:43 -0400475 private boolean hasStatusText() {
476 return mNextAlarmTextView.getVisibility() == View.VISIBLE
477 || mRingerModeTextView.getVisibility() == View.VISIBLE;
478 }
479
Rohan Shahd3cf7562018-02-23 11:12:28 -0800480 /**
481 * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
482 * the space).
483 */
484 public void showLongPressTooltip() {
Amin Shaikh761c5662018-03-27 16:55:43 -0400485 // If we have status text to show, don't bother fading in the tooltip.
486 if (hasStatusText()) {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800487 return;
488 }
489
490 if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
491 mLongPressTooltipView.animate().cancel();
492 mLongPressTooltipView.setVisibility(View.VISIBLE);
493 mLongPressTooltipView.animate()
494 .alpha(1f)
495 .setDuration(FADE_ANIMATION_DURATION_MS)
496 .setListener(new AnimatorListenerAdapter() {
497 @Override
498 public void onAnimationEnd(Animator animation) {
499 mHandler.postDelayed(
500 mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
501 }
502 })
503 .start();
504
505 // Increment and drop the shown count in prefs for the next time we're deciding to
506 // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
507 // in prefs (say, from a long press).
508 if (getStoredShownCount() <= mShownCount) {
509 Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
510 }
511 }
512 }
513
514 /**
515 * Fades out the long press tooltip if it's partially visible - short circuits any running
Amin Shaikh761c5662018-03-27 16:55:43 -0400516 * animation. Additionally has the ability to fade in the status info text.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800517 *
Amin Shaikh761c5662018-03-27 16:55:43 -0400518 * @param shouldShowStatusText whether we should fade in the status text
Rohan Shahd3cf7562018-02-23 11:12:28 -0800519 */
Amin Shaikh761c5662018-03-27 16:55:43 -0400520 private void hideLongPressTooltip(boolean shouldShowStatusText) {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800521 mLongPressTooltipView.animate().cancel();
522 if (mLongPressTooltipView.getVisibility() == View.VISIBLE
523 && mLongPressTooltipView.getAlpha() != 0f) {
524 mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
525 mLongPressTooltipView.animate()
526 .alpha(0f)
527 .setDuration(FADE_ANIMATION_DURATION_MS)
528 .setListener(new AnimatorListenerAdapter() {
529 @Override
530 public void onAnimationEnd(Animator animation) {
Rohan Shaha160ab12018-03-09 15:47:36 -0800531 if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
Rohan Shahd3cf7562018-02-23 11:12:28 -0800532 mLongPressTooltipView.setVisibility(View.INVISIBLE);
533
Amin Shaikh761c5662018-03-27 16:55:43 -0400534 if (shouldShowStatusText) {
535 showStatus();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800536 }
537 }
538 })
539 .start();
540 } else {
541 mLongPressTooltipView.setVisibility(View.INVISIBLE);
Amin Shaikh761c5662018-03-27 16:55:43 -0400542 if (shouldShowStatusText) {
543 showStatus();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800544 }
545 }
546 }
547
548 /**
Amin Shaikh761c5662018-03-27 16:55:43 -0400549 * Fades in the updated status text. Note that if there's already a status showing, this will
Fabian Kozynski3f789332018-09-04 16:42:43 -0400550 * immediately fade it out and fade in the updated status.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800551 */
Amin Shaikh761c5662018-03-27 16:55:43 -0400552 private void showStatus() {
553 mStatusContainer.setAlpha(0f);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800554
Amin Shaikh761c5662018-03-27 16:55:43 -0400555 mStatusContainer.animate()
Rohan Shahd3cf7562018-02-23 11:12:28 -0800556 .alpha(1f)
557 .setDuration(FADE_ANIMATION_DURATION_MS)
558 .start();
559 }
560
Fabian Kozynski3f789332018-09-04 16:42:43 -0400561 /** Fades out the status text. */
Amin Shaikh761c5662018-03-27 16:55:43 -0400562 private void hideStatusText() {
Fabian Kozynski3f789332018-09-04 16:42:43 -0400563 mStatusContainer.animate()
564 .alpha(0f)
565 .setDuration(FADE_ANIMATION_DURATION_MS)
566 .start();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800567 }
568
Jason Monke5b770e2017-03-03 21:49:29 -0500569 public void updateEverything() {
Jason Monk1fdde2d2017-03-08 09:39:21 -0500570 post(() -> setClickable(false));
Jason Monke5b770e2017-03-03 21:49:29 -0500571 }
572
573 public void setQSPanel(final QSPanel qsPanel) {
574 mQsPanel = qsPanel;
575 setupHost(qsPanel.getHost());
576 }
577
578 public void setupHost(final QSTileHost host) {
579 mHost = host;
580 //host.setHeaderView(mExpandIndicator);
581 mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
582 mHeaderQsPanel.setHost(host, null /* No customization in header */);
Evan Lairdef160f22018-01-29 14:08:45 -0500583
584 // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000585 mBatteryMeterView.setColorsFromContext(mHost.getContext());
586 mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Jason Monke5b770e2017-03-03 21:49:29 -0500587 }
588
589 public void setCallback(Callback qsPanelCallback) {
590 mHeaderQsPanel.setCallback(qsPanelCallback);
591 }
Lucas Dupin1f7374a2018-02-26 18:08:33 -0800592
593 private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
594 if (info == null) {
595 return "";
596 }
597 String skeleton = android.text.format.DateFormat
598 .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
599 String pattern = android.text.format.DateFormat
600 .getBestDateTimePattern(Locale.getDefault(), skeleton);
601 return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
602 }
Amin Shaikh0e003312018-03-08 11:39:01 -0500603
604 public static float getColorIntensity(@ColorInt int color) {
605 return color == Color.WHITE ? 0 : 1;
606 }
Amin Shaikh50d2d1e2018-04-25 14:48:13 -0400607
608 public void setMargins(int sideMargins) {
609 for (int i = 0; i < getChildCount(); i++) {
610 View v = getChildAt(i);
611 if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
612 continue;
613 }
614 RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
615 lp.leftMargin = sideMargins;
616 lp.rightMargin = sideMargins;
617 }
618 }
Jason Monke5b770e2017-03-03 21:49:29 -0500619}