blob: 3ee6195858d62a434a08b97e643e4a4a479d8515 [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;
Fabian Kozynski12638242018-10-12 15:33:41 -040024import android.app.Dialog;
Amin Shaikh761c5662018-03-27 16:55:43 -040025import android.content.BroadcastReceiver;
Jason Monke5b770e2017-03-03 21:49:29 -050026import android.content.Context;
Evan Laird4ea2a492018-01-22 11:29:12 -050027import android.content.Intent;
Amin Shaikh761c5662018-03-27 16:55:43 -040028import android.content.IntentFilter;
Jason Monke5b770e2017-03-03 21:49:29 -050029import android.content.res.Configuration;
Rohan Shahcc3d1d82018-03-30 21:24:17 +000030import android.content.res.Resources;
Jason Monke5b770e2017-03-03 21:49:29 -050031import android.graphics.Color;
32import android.graphics.Rect;
Amin Shaikh761c5662018-03-27 16:55:43 -040033import android.media.AudioManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080034import android.os.Handler;
Fabian Kozynski12638242018-10-12 15:33:41 -040035import android.os.Looper;
Evan Laird4ea2a492018-01-22 11:29:12 -050036import android.provider.AlarmClock;
Beverlyb9826dd2018-04-12 15:02:45 -040037import android.service.notification.ZenModeConfig;
Rohan Shahd3cf7562018-02-23 11:12:28 -080038import android.text.format.DateUtils;
Jason Monke5b770e2017-03-03 21:49:29 -050039import android.util.AttributeSet;
Rohan Shaha160ab12018-03-09 15:47:36 -080040import android.util.Log;
Adrian Roos13836052018-03-15 21:06:37 +010041import android.util.Pair;
Jason Monk824ffff2017-04-11 15:49:06 -040042import android.view.View;
Adrian Roos13836052018-03-15 21:06:37 +010043import android.view.WindowInsets;
Fabian Kozynski12638242018-10-12 15:33:41 -040044import android.view.WindowManager;
Beverlyff2df9b2018-10-10 16:54:10 -040045import android.widget.FrameLayout;
Amin Shaikh761c5662018-03-27 16:55:43 -040046import android.widget.ImageView;
Jason Monke5b770e2017-03-03 21:49:29 -050047import android.widget.RelativeLayout;
Rohan Shahd3cf7562018-02-23 11:12:28 -080048import android.widget.TextView;
Jason Monke5b770e2017-03-03 21:49:29 -050049
Beverlyff2df9b2018-10-10 16:54:10 -040050import androidx.annotation.VisibleForTesting;
51
Jason Monke5b770e2017-03-03 21:49:29 -050052import com.android.settingslib.Utils;
53import com.android.systemui.BatteryMeterView;
54import com.android.systemui.Dependency;
Rohan Shahd3cf7562018-02-23 11:12:28 -080055import com.android.systemui.Prefs;
Jason Monke5b770e2017-03-03 21:49:29 -050056import com.android.systemui.R;
57import com.android.systemui.plugins.ActivityStarter;
Fabian Kozynski12638242018-10-12 15:33:41 -040058import com.android.systemui.privacy.OngoingPrivacyChip;
59import com.android.systemui.privacy.OngoingPrivacyDialog;
Jason Monke5b770e2017-03-03 21:49:29 -050060import com.android.systemui.qs.QSDetail.Callback;
Adrian Roos13836052018-03-15 21:06:37 +010061import com.android.systemui.statusbar.phone.PhoneStatusBarView;
Evan Laird95896952018-01-22 19:30:05 -050062import com.android.systemui.statusbar.phone.StatusBarIconController;
63import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
Evan Laird20b87bf2018-04-12 09:54:11 -040064import com.android.systemui.statusbar.phone.StatusIconContainer;
Fabian Kozynski12638242018-10-12 15:33:41 -040065import com.android.systemui.statusbar.phone.SystemUIDialog;
Beverlyff2df9b2018-10-10 16:54:10 -040066import com.android.systemui.statusbar.policy.Clock;
Evan Laird39254d42018-01-18 16:05:30 -050067import com.android.systemui.statusbar.policy.DarkIconDispatcher;
Jason Monk824ffff2017-04-11 15:49:06 -040068import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Rohan Shahcc3d1d82018-03-30 21:24:17 +000069import com.android.systemui.statusbar.policy.DateView;
Rohan Shahd3cf7562018-02-23 11:12:28 -080070import com.android.systemui.statusbar.policy.NextAlarmController;
Beverlyb9826dd2018-04-12 15:02:45 -040071import com.android.systemui.statusbar.policy.ZenModeController;
Jason Monke5b770e2017-03-03 21:49:29 -050072
Lucas Dupin1f7374a2018-02-26 18:08:33 -080073import java.util.Locale;
Beverlyb9826dd2018-04-12 15:02:45 -040074import java.util.Objects;
Lucas Dupin1f7374a2018-02-26 18:08:33 -080075
Rohan Shahd3cf7562018-02-23 11:12:28 -080076/**
77 * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
78 * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
79 * contents.
80 */
Amin Shaikhf09450b2018-04-06 17:32:45 -040081public class QuickStatusBarHeader extends RelativeLayout implements
Beverlyb9826dd2018-04-12 15:02:45 -040082 View.OnClickListener, NextAlarmController.NextAlarmChangeCallback,
83 ZenModeController.Callback {
Rohan Shaha160ab12018-03-09 15:47:36 -080084 private static final String TAG = "QuickStatusBarHeader";
85 private static final boolean DEBUG = false;
Jason Monke5b770e2017-03-03 21:49:29 -050086
Rohan Shahd3cf7562018-02-23 11:12:28 -080087 /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
88 private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
89 private static final int FADE_ANIMATION_DURATION_MS = 300;
90 private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
Rohan Shahb6a915c2018-03-01 11:57:00 -080091 public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
Rohan Shahd3cf7562018-02-23 11:12:28 -080092
93 private final Handler mHandler = new Handler();
Jason Monke5b770e2017-03-03 21:49:29 -050094
95 private QSPanel mQsPanel;
96
97 private boolean mExpanded;
98 private boolean mListening;
Charles Hece2a7c02017-10-11 20:25:20 +010099 private boolean mQsDisabled;
Jason Monke5b770e2017-03-03 21:49:29 -0500100
101 protected QuickQSPanel mHeaderQsPanel;
102 protected QSTileHost mHost;
Evan Laird95896952018-01-22 19:30:05 -0500103 private TintedIconManager mIconManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800104 private TouchAnimator mStatusIconsAlphaAnimator;
105 private TouchAnimator mHeaderTextContainerAlphaAnimator;
Evan Laird95896952018-01-22 19:30:05 -0500106
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000107 private View mSystemIconsView;
Evan Laird95896952018-01-22 19:30:05 -0500108 private View mQuickQsStatusIcons;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800109 private View mHeaderTextContainerView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400110 /** View containing the next alarm and ringer mode info. */
111 private View mStatusContainer;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800112 /** Tooltip for educating users that they can long press on icons to see more details. */
113 private View mLongPressTooltipView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400114
115 private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
116 private AlarmManager.AlarmClockInfo mNextAlarm;
117
118 private ImageView mNextAlarmIcon;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800119 /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
120 private TextView mNextAlarmTextView;
Amin Shaikh761c5662018-03-27 16:55:43 -0400121 private View mStatusSeparator;
122 private ImageView mRingerModeIcon;
123 private TextView mRingerModeTextView;
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000124 private BatteryMeterView mBatteryMeterView;
125 private Clock mClockView;
126 private DateView mDateView;
Fabian Kozynski12638242018-10-12 15:33:41 -0400127 private OngoingPrivacyChip mPrivacyChip;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800128
129 private NextAlarmController mAlarmController;
Beverlyb9826dd2018-04-12 15:02:45 -0400130 private ZenModeController mZenController;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800131 /** Counts how many times the long press tooltip has been shown to the user. */
132 private int mShownCount;
133
Amin Shaikh761c5662018-03-27 16:55:43 -0400134 private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
135 @Override
136 public void onReceive(Context context, Intent intent) {
137 mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
138 updateStatusText();
139 }
140 };
141
Rohan Shahd3cf7562018-02-23 11:12:28 -0800142 /**
143 * Runnable for automatically fading out the long press tooltip (as if it were animating away).
144 */
145 private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
Evan Laird4ea2a492018-01-22 11:29:12 -0500146
Jason Monke5b770e2017-03-03 21:49:29 -0500147 public QuickStatusBarHeader(Context context, AttributeSet attrs) {
148 super(context, attrs);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800149 mAlarmController = Dependency.get(NextAlarmController.class);
Beverlyb9826dd2018-04-12 15:02:45 -0400150 mZenController = Dependency.get(ZenModeController.class);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800151 mShownCount = getStoredShownCount();
Jason Monke5b770e2017-03-03 21:49:29 -0500152 }
153
154 @Override
155 protected void onFinishInflate() {
156 super.onFinishInflate();
Jason Monke5b770e2017-03-03 21:49:29 -0500157
158 mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000159 mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
Evan Laird95896952018-01-22 19:30:05 -0500160 mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
Evan Laird20b87bf2018-04-12 09:54:11 -0400161 StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
162 iconContainer.setShouldRestrictIcons(false);
163 mIconManager = new TintedIconManager(iconContainer);
Jason Monke5b770e2017-03-03 21:49:29 -0500164
Rohan Shahd3cf7562018-02-23 11:12:28 -0800165 // Views corresponding to the header info section (e.g. tooltip and next alarm).
166 mHeaderTextContainerView = findViewById(R.id.header_text_container);
167 mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
Amin Shaikh761c5662018-03-27 16:55:43 -0400168 mStatusContainer = findViewById(R.id.status_container);
169 mStatusSeparator = findViewById(R.id.status_separator);
170 mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800171 mNextAlarmTextView = findViewById(R.id.next_alarm_text);
Amin Shaikh761c5662018-03-27 16:55:43 -0400172 mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
173 mRingerModeTextView = findViewById(R.id.ringer_mode_text);
Jason Monke5b770e2017-03-03 21:49:29 -0500174
175 updateResources();
176
Jason Monk824ffff2017-04-11 15:49:06 -0400177 Rect tintArea = new Rect(0, 0, 0, 0);
Jason Changb4e879d2018-04-11 11:17:58 +0800178 int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
179 android.R.attr.colorForeground);
Amin Shaikh0e003312018-03-08 11:39:01 -0500180 float intensity = getColorIntensity(colorForeground);
Evan Laird95896952018-01-22 19:30:05 -0500181 int fillColor = fillColorForIntensity(intensity, getContext());
182
183 // Set light text on the header icons because they will always be on a black background
Evan Laird39254d42018-01-18 16:05:30 -0500184 applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Evan Laird95896952018-01-22 19:30:05 -0500185
186 // Set the correct tint for the status icons so they contrast
187 mIconManager.setTint(fillColor);
Jason Monke5b770e2017-03-03 21:49:29 -0500188
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000189 mBatteryMeterView = findViewById(R.id.battery);
190 mBatteryMeterView.setForceShowPercent(true);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400191 mBatteryMeterView.setOnClickListener(this);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000192 mClockView = findViewById(R.id.clock);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400193 mClockView.setOnClickListener(this);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000194 mDateView = findViewById(R.id.date);
Fabian Kozynski12638242018-10-12 15:33:41 -0400195 mPrivacyChip = findViewById(R.id.privacy_chip);
196 mPrivacyChip.setOnClickListener(this);
Jason Monke5b770e2017-03-03 21:49:29 -0500197 }
198
Amin Shaikh761c5662018-03-27 16:55:43 -0400199 private void updateStatusText() {
Beverlyb9826dd2018-04-12 15:02:45 -0400200 boolean changed = updateRingerStatus() || updateAlarmStatus();
201
202 if (changed) {
203 boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
204 boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
205 mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
206 : View.GONE);
207 updateTooltipShow();
208 }
209 }
210
211 private boolean updateRingerStatus() {
212 boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
213 CharSequence originalRingerText = mRingerModeTextView.getText();
214
Amin Shaikh761c5662018-03-27 16:55:43 -0400215 boolean ringerVisible = false;
Beverlyb9826dd2018-04-12 15:02:45 -0400216 if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(),
Beverlyff2df9b2018-10-10 16:54:10 -0400217 mZenController.getConsolidatedPolicy())) {
Beverlyb9826dd2018-04-12 15:02:45 -0400218 if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
219 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_vibrate);
220 mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
221 ringerVisible = true;
222 } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) {
223 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_silent);
224 mRingerModeTextView.setText(R.string.qs_status_phone_muted);
225 ringerVisible = true;
226 }
Amin Shaikh761c5662018-03-27 16:55:43 -0400227 }
228 mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
229 mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
230
Beverlyb9826dd2018-04-12 15:02:45 -0400231 return isOriginalVisible != ringerVisible ||
232 !Objects.equals(originalRingerText, mRingerModeTextView.getText());
233 }
234
235 private boolean updateAlarmStatus() {
236 boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
237 CharSequence originalAlarmText = mNextAlarmTextView.getText();
238
Amin Shaikh761c5662018-03-27 16:55:43 -0400239 boolean alarmVisible = false;
240 if (mNextAlarm != null) {
241 alarmVisible = true;
242 mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm));
243 }
244 mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
245 mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
Amin Shaikh761c5662018-03-27 16:55:43 -0400246
Beverlyb9826dd2018-04-12 15:02:45 -0400247 return isOriginalVisible != alarmVisible ||
248 !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
249 }
Amin Shaikh761c5662018-03-27 16:55:43 -0400250
Jason Monk824ffff2017-04-11 15:49:06 -0400251 private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
252 View v = findViewById(id);
253 if (v instanceof DarkReceiver) {
254 ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
255 }
256 }
257
Evan Laird95896952018-01-22 19:30:05 -0500258 private int fillColorForIntensity(float intensity, Context context) {
259 if (intensity == 0) {
Amin Shaikh9cac3382018-04-11 17:10:07 -0400260 return context.getColor(R.color.light_mode_icon_color_single_tone);
Evan Laird95896952018-01-22 19:30:05 -0500261 }
Amin Shaikh9cac3382018-04-11 17:10:07 -0400262 return context.getColor(R.color.dark_mode_icon_color_single_tone);
Evan Laird95896952018-01-22 19:30:05 -0500263 }
264
Jason Monke5b770e2017-03-03 21:49:29 -0500265 @Override
266 protected void onConfigurationChanged(Configuration newConfig) {
267 super.onConfigurationChanged(newConfig);
268 updateResources();
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000269
270 // Update color schemes in landscape to use wallpaperTextColor
271 boolean shouldUseWallpaperTextColor =
272 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
273 mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
274 mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
Fabian Kozynski12638242018-10-12 15:33:41 -0400275
276 MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
277 int sideMargins = lm.leftMargin;
278 int topBottomMargins = (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
279 ? 0 : sideMargins;
280 lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
281 mPrivacyChip.setLayoutParams(lm);
Jason Monke5b770e2017-03-03 21:49:29 -0500282 }
283
284 @Override
285 public void onRtlPropertiesChanged(int layoutDirection) {
286 super.onRtlPropertiesChanged(layoutDirection);
287 updateResources();
288 }
289
Evan Laird84b98952018-07-02 17:43:59 -0400290 /**
291 * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
292 * when there is a notch involved the status bar can remain a fixed pixel size.
293 */
294 private void updateMinimumHeight() {
295 int sbHeight = mContext.getResources().getDimensionPixelSize(
296 com.android.internal.R.dimen.status_bar_height);
297 int qqsHeight = mContext.getResources().getDimensionPixelSize(
298 R.dimen.qs_quick_header_panel_height);
299
300 setMinimumHeight(sbHeight + qqsHeight);
301 }
302
Jason Monke5b770e2017-03-03 21:49:29 -0500303 private void updateResources() {
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000304 Resources resources = mContext.getResources();
Evan Laird84b98952018-07-02 17:43:59 -0400305 updateMinimumHeight();
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000306
307 // Update height for a few views, especially due to landscape mode restricting space.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800308 mHeaderTextContainerView.getLayoutParams().height =
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000309 resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800310 mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
311
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000312 mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
313 com.android.internal.R.dimen.quick_qs_offset_height);
314 mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
315
Evan Laird84b98952018-07-02 17:43:59 -0400316 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
317 if (mQsDisabled) {
318 lp.height = resources.getDimensionPixelSize(
319 com.android.internal.R.dimen.quick_qs_offset_height);
320 } else {
321 lp.height = Math.max(getMinimumHeight(),
322 resources.getDimensionPixelSize(
Evan Lairdbf3794a2018-07-17 15:55:40 -0400323 com.android.internal.R.dimen.quick_qs_total_height));
Evan Laird84b98952018-07-02 17:43:59 -0400324 }
325
326 setLayoutParams(lp);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000327
Rohan Shahd3cf7562018-02-23 11:12:28 -0800328 updateStatusIconAlphaAnimator();
329 updateHeaderTextContainerAlphaAnimator();
Evan Laird95896952018-01-22 19:30:05 -0500330 }
331
Rohan Shahd3cf7562018-02-23 11:12:28 -0800332 private void updateStatusIconAlphaAnimator() {
333 mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
Evan Laird95896952018-01-22 19:30:05 -0500334 .addFloat(mQuickQsStatusIcons, "alpha", 1, 0)
335 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500336 }
337
Rohan Shahd3cf7562018-02-23 11:12:28 -0800338 private void updateHeaderTextContainerAlphaAnimator() {
339 mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
340 .addFloat(mHeaderTextContainerView, "alpha", 0, 1)
341 .setStartDelay(.5f)
342 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500343 }
344
345 public void setExpanded(boolean expanded) {
346 if (mExpanded == expanded) return;
347 mExpanded = expanded;
348 mHeaderQsPanel.setExpanded(expanded);
349 updateEverything();
350 }
351
Rohan Shahd3cf7562018-02-23 11:12:28 -0800352 /**
353 * Animates the inner contents based on the given expansion details.
354 *
355 * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen)
356 * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
357 * @param panelTranslationY how much the panel has physically moved down vertically (required
358 * for keyguard animations only)
359 */
360 public void setExpansion(boolean isKeyguardShowing, float expansionFraction,
361 float panelTranslationY) {
362 final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction;
363 if (mStatusIconsAlphaAnimator != null) {
364 mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
Evan Laird95896952018-01-22 19:30:05 -0500365 }
Rohan Shahd3cf7562018-02-23 11:12:28 -0800366
367 if (isKeyguardShowing) {
368 // If the keyguard is showing, we want to offset the text so that it comes in at the
369 // same time as the panel as it slides down.
370 mHeaderTextContainerView.setTranslationY(panelTranslationY);
371 } else {
372 mHeaderTextContainerView.setTranslationY(0f);
373 }
374
375 if (mHeaderTextContainerAlphaAnimator != null) {
376 mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
377 }
378
379 // Check the original expansion fraction - we don't want to show the tooltip until the
380 // panel is pulled all the way out.
381 if (expansionFraction == 1f) {
382 // QS is fully expanded, bring in the tooltip.
383 showLongPressTooltip();
384 }
385 }
386
387 /** Returns the latest stored tooltip shown count from SharedPreferences. */
388 private int getStoredShownCount() {
389 return Prefs.getInt(
390 mContext,
391 Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
392 TOOLTIP_NOT_YET_SHOWN_COUNT);
Jason Monke5b770e2017-03-03 21:49:29 -0500393 }
394
Charles Hece2a7c02017-10-11 20:25:20 +0100395 public void disable(int state1, int state2, boolean animate) {
396 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
397 if (disabled == mQsDisabled) return;
398 mQsDisabled = disabled;
399 mHeaderQsPanel.setDisabledByPolicy(disabled);
Amin Shaikhf09450b2018-04-06 17:32:45 -0400400 mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
401 mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
402 updateResources();
Charles Hece2a7c02017-10-11 20:25:20 +0100403 }
404
405 @Override
406 public void onAttachedToWindow() {
Amin Shaikha59215a2018-04-23 08:52:38 -0400407 super.onAttachedToWindow();
Evan Laird95896952018-01-22 19:30:05 -0500408 Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
Adrian Roos13836052018-03-15 21:06:37 +0100409 requestApplyInsets();
410 }
411
412 @Override
413 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
414 Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
415 insets.getDisplayCutout(), getDisplay());
416 if (padding == null) {
Amin Shaikh7d86b042018-04-16 16:00:12 -0400417 mSystemIconsView.setPaddingRelative(
418 getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
419 getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
Adrian Roos13836052018-03-15 21:06:37 +0100420 } else {
Amin Shaikh7d86b042018-04-16 16:00:12 -0400421 mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
422
Adrian Roos13836052018-03-15 21:06:37 +0100423 }
424 return super.onApplyWindowInsets(insets);
Charles Hece2a7c02017-10-11 20:25:20 +0100425 }
426
427 @Override
Jason Monke5b770e2017-03-03 21:49:29 -0500428 @VisibleForTesting
429 public void onDetachedFromWindow() {
430 setListening(false);
Evan Laird95896952018-01-22 19:30:05 -0500431 Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
Jason Monke5b770e2017-03-03 21:49:29 -0500432 super.onDetachedFromWindow();
433 }
434
435 public void setListening(boolean listening) {
436 if (listening == mListening) {
437 return;
438 }
439 mHeaderQsPanel.setListening(listening);
Fabian Kozynski12638242018-10-12 15:33:41 -0400440 mPrivacyChip.setListening(listening);
Jason Monke5b770e2017-03-03 21:49:29 -0500441 mListening = listening;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800442
443 if (listening) {
Beverlyb9826dd2018-04-12 15:02:45 -0400444 mZenController.addCallback(this);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800445 mAlarmController.addCallback(this);
Amin Shaikh761c5662018-03-27 16:55:43 -0400446 mContext.registerReceiver(mRingerReceiver,
447 new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
Rohan Shahd3cf7562018-02-23 11:12:28 -0800448 } else {
Beverlyb9826dd2018-04-12 15:02:45 -0400449 mZenController.removeCallback(this);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800450 mAlarmController.removeCallback(this);
Amin Shaikh761c5662018-03-27 16:55:43 -0400451 mContext.unregisterReceiver(mRingerReceiver);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800452 }
Jason Monke5b770e2017-03-03 21:49:29 -0500453 }
454
Evan Laird95896952018-01-22 19:30:05 -0500455 @Override
456 public void onClick(View v) {
Amin Shaikh39bdc502018-05-15 23:43:34 -0400457 if (v == mClockView) {
Evan Laird95896952018-01-22 19:30:05 -0500458 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
459 AlarmClock.ACTION_SHOW_ALARMS),0);
Amin Shaikh39bdc502018-05-15 23:43:34 -0400460 } else if (v == mBatteryMeterView) {
461 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
462 Intent.ACTION_POWER_USAGE_SUMMARY),0);
Fabian Kozynski12638242018-10-12 15:33:41 -0400463 } else if (v == mPrivacyChip) {
464 Handler mUiHandler = new Handler(Looper.getMainLooper());
465 mUiHandler.post(() -> {
466 Dialog mDialog = new OngoingPrivacyDialog(mContext,
467 mPrivacyChip.getBuilder()).createDialog();
468 mDialog.getWindow().setType(
469 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
470 SystemUIDialog.setShowForAllUsers(mDialog, true);
471 SystemUIDialog.registerDismissListener(mDialog);
472 SystemUIDialog.setWindowOnTop(mDialog);
473 mUiHandler.post(() -> mDialog.show());
474 mHost.collapsePanels();
475 });
Evan Laird95896952018-01-22 19:30:05 -0500476 }
477 }
478
Rohan Shahd3cf7562018-02-23 11:12:28 -0800479 @Override
480 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
Amin Shaikh761c5662018-03-27 16:55:43 -0400481 mNextAlarm = nextAlarm;
482 updateStatusText();
483 }
Rohan Shaha160ab12018-03-09 15:47:36 -0800484
Beverlyb9826dd2018-04-12 15:02:45 -0400485 @Override
486 public void onZenChanged(int zen) {
487 updateStatusText();
488
489 }
490
491 @Override
492 public void onConfigChanged(ZenModeConfig config) {
493 updateStatusText();
494 }
495
Amin Shaikh761c5662018-03-27 16:55:43 -0400496 private void updateTooltipShow() {
497 if (hasStatusText()) {
498 hideLongPressTooltip(true /* shouldShowStatusText */);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800499 } else {
Amin Shaikh761c5662018-03-27 16:55:43 -0400500 hideStatusText();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800501 }
502 updateHeaderTextContainerAlphaAnimator();
503 }
504
Amin Shaikh761c5662018-03-27 16:55:43 -0400505 private boolean hasStatusText() {
506 return mNextAlarmTextView.getVisibility() == View.VISIBLE
507 || mRingerModeTextView.getVisibility() == View.VISIBLE;
508 }
509
Rohan Shahd3cf7562018-02-23 11:12:28 -0800510 /**
511 * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
512 * the space).
513 */
514 public void showLongPressTooltip() {
Amin Shaikh761c5662018-03-27 16:55:43 -0400515 // If we have status text to show, don't bother fading in the tooltip.
516 if (hasStatusText()) {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800517 return;
518 }
519
520 if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
521 mLongPressTooltipView.animate().cancel();
522 mLongPressTooltipView.setVisibility(View.VISIBLE);
523 mLongPressTooltipView.animate()
524 .alpha(1f)
525 .setDuration(FADE_ANIMATION_DURATION_MS)
526 .setListener(new AnimatorListenerAdapter() {
527 @Override
528 public void onAnimationEnd(Animator animation) {
529 mHandler.postDelayed(
530 mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
531 }
532 })
533 .start();
534
535 // Increment and drop the shown count in prefs for the next time we're deciding to
536 // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
537 // in prefs (say, from a long press).
538 if (getStoredShownCount() <= mShownCount) {
539 Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
540 }
541 }
542 }
543
544 /**
545 * Fades out the long press tooltip if it's partially visible - short circuits any running
Amin Shaikh761c5662018-03-27 16:55:43 -0400546 * animation. Additionally has the ability to fade in the status info text.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800547 *
Amin Shaikh761c5662018-03-27 16:55:43 -0400548 * @param shouldShowStatusText whether we should fade in the status text
Rohan Shahd3cf7562018-02-23 11:12:28 -0800549 */
Amin Shaikh761c5662018-03-27 16:55:43 -0400550 private void hideLongPressTooltip(boolean shouldShowStatusText) {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800551 mLongPressTooltipView.animate().cancel();
552 if (mLongPressTooltipView.getVisibility() == View.VISIBLE
553 && mLongPressTooltipView.getAlpha() != 0f) {
554 mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
555 mLongPressTooltipView.animate()
556 .alpha(0f)
557 .setDuration(FADE_ANIMATION_DURATION_MS)
558 .setListener(new AnimatorListenerAdapter() {
559 @Override
560 public void onAnimationEnd(Animator animation) {
Rohan Shaha160ab12018-03-09 15:47:36 -0800561 if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
Rohan Shahd3cf7562018-02-23 11:12:28 -0800562 mLongPressTooltipView.setVisibility(View.INVISIBLE);
563
Amin Shaikh761c5662018-03-27 16:55:43 -0400564 if (shouldShowStatusText) {
565 showStatus();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800566 }
567 }
568 })
569 .start();
570 } else {
571 mLongPressTooltipView.setVisibility(View.INVISIBLE);
Amin Shaikh761c5662018-03-27 16:55:43 -0400572 if (shouldShowStatusText) {
573 showStatus();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800574 }
575 }
576 }
577
578 /**
Amin Shaikh761c5662018-03-27 16:55:43 -0400579 * Fades in the updated status text. Note that if there's already a status showing, this will
Fabian Kozynski3f789332018-09-04 16:42:43 -0400580 * immediately fade it out and fade in the updated status.
Rohan Shahd3cf7562018-02-23 11:12:28 -0800581 */
Amin Shaikh761c5662018-03-27 16:55:43 -0400582 private void showStatus() {
583 mStatusContainer.setAlpha(0f);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800584
Amin Shaikh761c5662018-03-27 16:55:43 -0400585 mStatusContainer.animate()
Rohan Shahd3cf7562018-02-23 11:12:28 -0800586 .alpha(1f)
587 .setDuration(FADE_ANIMATION_DURATION_MS)
588 .start();
589 }
590
Fabian Kozynski3f789332018-09-04 16:42:43 -0400591 /** Fades out the status text. */
Amin Shaikh761c5662018-03-27 16:55:43 -0400592 private void hideStatusText() {
Fabian Kozynski3f789332018-09-04 16:42:43 -0400593 mStatusContainer.animate()
594 .alpha(0f)
595 .setDuration(FADE_ANIMATION_DURATION_MS)
596 .start();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800597 }
598
Jason Monke5b770e2017-03-03 21:49:29 -0500599 public void updateEverything() {
Jason Monk1fdde2d2017-03-08 09:39:21 -0500600 post(() -> setClickable(false));
Jason Monke5b770e2017-03-03 21:49:29 -0500601 }
602
603 public void setQSPanel(final QSPanel qsPanel) {
604 mQsPanel = qsPanel;
605 setupHost(qsPanel.getHost());
606 }
607
608 public void setupHost(final QSTileHost host) {
609 mHost = host;
610 //host.setHeaderView(mExpandIndicator);
611 mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
612 mHeaderQsPanel.setHost(host, null /* No customization in header */);
Evan Lairdef160f22018-01-29 14:08:45 -0500613
614 // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000615 mBatteryMeterView.setColorsFromContext(mHost.getContext());
616 mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Jason Monke5b770e2017-03-03 21:49:29 -0500617 }
618
619 public void setCallback(Callback qsPanelCallback) {
620 mHeaderQsPanel.setCallback(qsPanelCallback);
621 }
Lucas Dupin1f7374a2018-02-26 18:08:33 -0800622
623 private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
624 if (info == null) {
625 return "";
626 }
627 String skeleton = android.text.format.DateFormat
628 .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
629 String pattern = android.text.format.DateFormat
630 .getBestDateTimePattern(Locale.getDefault(), skeleton);
631 return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
632 }
Amin Shaikh0e003312018-03-08 11:39:01 -0500633
634 public static float getColorIntensity(@ColorInt int color) {
635 return color == Color.WHITE ? 0 : 1;
636 }
Amin Shaikh50d2d1e2018-04-25 14:48:13 -0400637
638 public void setMargins(int sideMargins) {
639 for (int i = 0; i < getChildCount(); i++) {
640 View v = getChildAt(i);
641 if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
642 continue;
643 }
644 RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
645 lp.leftMargin = sideMargins;
646 lp.rightMargin = sideMargins;
647 }
648 }
Jason Monke5b770e2017-03-03 21:49:29 -0500649}