blob: 8d50d4b71cce53e2385e14a41d7f91f694276f55 [file] [log] [blame]
Anthony Chen54daefe2017-04-07 17:19:54 -07001/*
2 * Copyright (C) 2017 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
17package com.android.systemui.qs;
18
Charles Hece2a7c02017-10-11 20:25:20 +010019import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
20
Anthony Chen54daefe2017-04-07 17:19:54 -070021import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
22
23import android.app.ActivityManager;
24import android.app.AlarmManager;
25import android.app.PendingIntent;
26import android.content.Context;
27import android.content.Intent;
28import android.content.res.Configuration;
29import android.content.res.Resources;
30import android.graphics.PorterDuff.Mode;
31import android.graphics.drawable.Drawable;
32import android.graphics.drawable.RippleDrawable;
33import android.os.UserManager;
34import android.provider.AlarmClock;
35import android.support.annotation.Nullable;
36import android.support.annotation.VisibleForTesting;
37import android.util.AttributeSet;
38import android.view.View;
39import android.view.View.OnClickListener;
40import android.widget.FrameLayout;
41import android.widget.ImageView;
Anthony Chen54daefe2017-04-07 17:19:54 -070042import android.widget.TextView;
43import android.widget.Toast;
44
45import com.android.internal.logging.MetricsLogger;
46import com.android.internal.logging.nano.MetricsProto;
47import com.android.keyguard.KeyguardStatusView;
Selim Cinek1a891a92017-12-04 17:41:27 +010048import com.android.keyguard.KeyguardUpdateMonitor;
Anthony Chen54daefe2017-04-07 17:19:54 -070049import com.android.settingslib.Utils;
Jason Monk826b6092017-08-29 11:30:52 -040050import com.android.settingslib.drawable.UserIconDrawable;
Anthony Chen54daefe2017-04-07 17:19:54 -070051import com.android.systemui.Dependency;
52import com.android.systemui.FontSizeUtils;
53import com.android.systemui.R;
54import com.android.systemui.R.dimen;
55import com.android.systemui.R.id;
Charles Hece2a7c02017-10-11 20:25:20 +010056import com.android.systemui.SysUiServiceProvider;
Anthony Chen54daefe2017-04-07 17:19:54 -070057import com.android.systemui.plugins.ActivityStarter;
58import com.android.systemui.qs.TouchAnimator.Builder;
Anthony Chen54daefe2017-04-07 17:19:54 -070059import com.android.systemui.qs.TouchAnimator.ListenerAdapter;
Charles Hece2a7c02017-10-11 20:25:20 +010060import com.android.systemui.statusbar.CommandQueue;
Anthony Chen54daefe2017-04-07 17:19:54 -070061import com.android.systemui.statusbar.phone.ExpandableIndicator;
62import com.android.systemui.statusbar.phone.MultiUserSwitch;
63import com.android.systemui.statusbar.phone.SettingsButton;
64import com.android.systemui.statusbar.policy.DeviceProvisionedController;
65import com.android.systemui.statusbar.policy.NetworkController;
66import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
67import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
68import com.android.systemui.statusbar.policy.NextAlarmController;
69import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
70import com.android.systemui.statusbar.policy.UserInfoController;
71import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
72import com.android.systemui.tuner.TunerService;
73
74public class QSFooterImpl extends FrameLayout implements QSFooter,
75 NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
Charles Hece2a7c02017-10-11 20:25:20 +010076 SignalCallback, CommandQueue.Callbacks {
Anthony Chen54daefe2017-04-07 17:19:54 -070077 private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
78
79 private ActivityStarter mActivityStarter;
80 private NextAlarmController mNextAlarmController;
81 private UserInfoController mUserInfoController;
82 private SettingsButton mSettingsButton;
83 protected View mSettingsContainer;
84
85 private TextView mAlarmStatus;
86 private View mAlarmStatusCollapsed;
87 private View mDate;
88
Charles Hece2a7c02017-10-11 20:25:20 +010089 private boolean mQsDisabled;
Anthony Chen54daefe2017-04-07 17:19:54 -070090 private QSPanel mQsPanel;
91
92 private boolean mExpanded;
93 private boolean mAlarmShowing;
94
95 protected ExpandableIndicator mExpandIndicator;
96
97 private boolean mListening;
98 private AlarmManager.AlarmClockInfo mNextAlarm;
99
100 private boolean mShowEmergencyCallsOnly;
101 protected MultiUserSwitch mMultiUserSwitch;
102 private ImageView mMultiUserAvatar;
103
104 protected TouchAnimator mSettingsAlpha;
105 private float mExpansionAmount;
106
107 protected View mEdit;
108 private TouchAnimator mAnimator;
109 private View mDateTimeGroup;
110 private boolean mKeyguardShowing;
111 private TouchAnimator mAlarmAnimator;
112
113 public QSFooterImpl(Context context, AttributeSet attrs) {
114 super(context, attrs);
115 }
116
117 @Override
118 protected void onFinishInflate() {
119 super.onFinishInflate();
120 Resources res = getResources();
121
122 mEdit = findViewById(android.R.id.edit);
123 mEdit.setOnClickListener(view ->
124 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
125 mQsPanel.showEdit(view)));
126
127 mDateTimeGroup = findViewById(id.date_time_alarm_group);
128 mDate = findViewById(R.id.date);
129
130 mExpandIndicator = findViewById(R.id.expand_indicator);
131 mSettingsButton = findViewById(R.id.settings_button);
132 mSettingsContainer = findViewById(R.id.settings_button_container);
133 mSettingsButton.setOnClickListener(this);
134
135 mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
136 mAlarmStatus = findViewById(R.id.alarm_status);
137 mDateTimeGroup.setOnClickListener(this);
138
139 mMultiUserSwitch = findViewById(R.id.multi_user_switch);
140 mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
141
142 // RenderThread is doing more harm than good when touching the header (to expand quick
143 // settings), so disable it for this view
144 ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
145 ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
146
147 updateResources();
148
149 mNextAlarmController = Dependency.get(NextAlarmController.class);
150 mUserInfoController = Dependency.get(UserInfoController.class);
151 mActivityStarter = Dependency.get(ActivityStarter.class);
152 addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
153 oldBottom) -> updateAnimator(right - left));
154 }
155
156 private void updateAnimator(int width) {
157 int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
158 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
159 - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
160 int remaining = (width - numTiles * size) / (numTiles - 1);
161 int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
162
163 mAnimator = new Builder()
Jason Monk6dadd3e42017-09-01 09:36:19 -0400164 .addFloat(mSettingsContainer, "translationX",
165 isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
Anthony Chen54daefe2017-04-07 17:19:54 -0700166 .addFloat(mSettingsButton, "rotation", -120, 0)
167 .build();
168 if (mAlarmShowing) {
Jason Monk5cdd5d92017-07-07 09:09:59 -0400169 int translate = isLayoutRtl() ? mDate.getWidth() : -mDate.getWidth();
Anthony Chen54daefe2017-04-07 17:19:54 -0700170 mAlarmAnimator = new Builder().addFloat(mDate, "alpha", 1, 0)
Jason Monk5cdd5d92017-07-07 09:09:59 -0400171 .addFloat(mDateTimeGroup, "translationX", 0, translate)
Anthony Chen54daefe2017-04-07 17:19:54 -0700172 .addFloat(mAlarmStatus, "alpha", 0, 1)
173 .setListener(new ListenerAdapter() {
174 @Override
175 public void onAnimationAtStart() {
176 mAlarmStatus.setVisibility(View.GONE);
177 }
178
179 @Override
180 public void onAnimationStarted() {
181 mAlarmStatus.setVisibility(View.VISIBLE);
182 }
183 }).build();
184 } else {
185 mAlarmAnimator = null;
186 mAlarmStatus.setVisibility(View.GONE);
187 mDate.setAlpha(1);
188 mDateTimeGroup.setTranslationX(0);
189 }
190 setExpansion(mExpansionAmount);
191 }
192
193 @Override
194 protected void onConfigurationChanged(Configuration newConfig) {
195 super.onConfigurationChanged(newConfig);
196 updateResources();
197 }
198
199 @Override
200 public void onRtlPropertiesChanged(int layoutDirection) {
201 super.onRtlPropertiesChanged(layoutDirection);
202 updateResources();
203 }
204
205 private void updateResources() {
206 FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
207
208 updateSettingsAnimator();
209 }
210
211 private void updateSettingsAnimator() {
212 mSettingsAlpha = createSettingsAlphaAnimator();
213
214 final boolean isRtl = isLayoutRtl();
215 if (isRtl && mDate.getWidth() == 0) {
216 mDate.addOnLayoutChangeListener(new OnLayoutChangeListener() {
217 @Override
218 public void onLayoutChange(View v, int left, int top, int right, int bottom,
219 int oldLeft, int oldTop, int oldRight, int oldBottom) {
220 mDate.setPivotX(getWidth());
221 mDate.removeOnLayoutChangeListener(this);
222 }
223 });
224 } else {
225 mDate.setPivotX(isRtl ? mDate.getWidth() : 0);
226 }
227 }
228
229 @Nullable
230 private TouchAnimator createSettingsAlphaAnimator() {
231 return new TouchAnimator.Builder()
232 .addFloat(mEdit, "alpha", 0, 1)
233 .addFloat(mMultiUserSwitch, "alpha", 0, 1)
234 .build();
235 }
236
237 @Override
238 public void setKeyguardShowing(boolean keyguardShowing) {
239 mKeyguardShowing = keyguardShowing;
240 setExpansion(mExpansionAmount);
241 }
242
243 @Override
244 public void setExpanded(boolean expanded) {
245 if (mExpanded == expanded) return;
246 mExpanded = expanded;
247 updateEverything();
248 }
249
250 @Override
251 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
252 mNextAlarm = nextAlarm;
253 if (nextAlarm != null) {
254 String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm);
255 mAlarmStatus.setText(alarmString);
256 mAlarmStatus.setContentDescription(mContext.getString(
257 R.string.accessibility_quick_settings_alarm, alarmString));
258 mAlarmStatusCollapsed.setContentDescription(mContext.getString(
259 R.string.accessibility_quick_settings_alarm, alarmString));
260 }
261 if (mAlarmShowing != (nextAlarm != null)) {
262 mAlarmShowing = nextAlarm != null;
263 updateAnimator(getWidth());
264 updateEverything();
265 }
266 }
267
268 @Override
269 public void setExpansion(float headerExpansionFraction) {
270 mExpansionAmount = headerExpansionFraction;
271 if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
272 if (mAlarmAnimator != null) mAlarmAnimator.setPosition(
273 mKeyguardShowing ? 0 : headerExpansionFraction);
274
275 if (mSettingsAlpha != null) {
276 mSettingsAlpha.setPosition(headerExpansionFraction);
277 }
278
279 updateAlarmVisibilities();
280
281 mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
282 }
283
284 @Override
Charles Hece2a7c02017-10-11 20:25:20 +0100285 public void onAttachedToWindow() {
286 super.onAttachedToWindow();
287 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
288 }
289
290 @Override
Anthony Chen54daefe2017-04-07 17:19:54 -0700291 @VisibleForTesting
292 public void onDetachedFromWindow() {
293 setListening(false);
Charles Hece2a7c02017-10-11 20:25:20 +0100294 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
Anthony Chen54daefe2017-04-07 17:19:54 -0700295 super.onDetachedFromWindow();
296 }
297
298 private void updateAlarmVisibilities() {
299 mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
300 }
301
302 @Override
303 public void setListening(boolean listening) {
304 if (listening == mListening) {
305 return;
306 }
307 mListening = listening;
308 updateListeners();
309 }
310
311 @Override
312 public View getExpandView() {
313 return findViewById(R.id.expand_indicator);
314 }
315
Charles Hece2a7c02017-10-11 20:25:20 +0100316 @Override
317 public void disable(int state1, int state2, boolean animate) {
318 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
319 if (disabled == mQsDisabled) return;
320 mQsDisabled = disabled;
321 updateEverything();
322 }
323
Anthony Chen54daefe2017-04-07 17:19:54 -0700324 public void updateEverything() {
325 post(() -> {
326 updateVisibilities();
327 setClickable(false);
328 });
329 }
330
331 private void updateVisibilities() {
332 updateAlarmVisibilities();
Charles Hece2a7c02017-10-11 20:25:20 +0100333
334 mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
Anthony Chen54daefe2017-04-07 17:19:54 -0700335 mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
336 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
Charles Hece2a7c02017-10-11 20:25:20 +0100337
338 mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
339
Anthony Chen54daefe2017-04-07 17:19:54 -0700340 final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
341
Benjamin Franzff66fa92017-08-10 10:39:44 +0100342
343 mMultiUserSwitch.setVisibility(mExpanded
344 && UserManager.get(mContext).isUserSwitcherEnabled()
Anthony Chen54daefe2017-04-07 17:19:54 -0700345 ? View.VISIBLE : View.INVISIBLE);
346
347 mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
348 }
349
350 private void updateListeners() {
351 if (mListening) {
352 mNextAlarmController.addCallback(this);
353 mUserInfoController.addCallback(this);
354 if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
355 Dependency.get(NetworkController.class).addEmergencyListener(this);
356 Dependency.get(NetworkController.class).addCallback(this);
357 }
358 } else {
359 mNextAlarmController.removeCallback(this);
360 mUserInfoController.removeCallback(this);
361 Dependency.get(NetworkController.class).removeEmergencyListener(this);
362 Dependency.get(NetworkController.class).removeCallback(this);
363 }
364 }
365
366 @Override
367 public void setQSPanel(final QSPanel qsPanel) {
368 mQsPanel = qsPanel;
369 if (mQsPanel != null) {
370 mMultiUserSwitch.setQsPanel(qsPanel);
371 }
372 }
373
374 @Override
375 public void onClick(View v) {
376 if (v == mSettingsButton) {
377 if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
378 // If user isn't setup just unlock the device and dump them back at SUW.
379 mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
380 return;
381 }
382 MetricsLogger.action(mContext,
383 mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
384 : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
385 if (mSettingsButton.isTunerClick()) {
386 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
387 if (TunerService.isTunerEnabled(mContext)) {
388 TunerService.showResetRequest(mContext, () -> {
389 // Relaunch settings so that the tuner disappears.
390 startSettingsActivity();
391 });
392 } else {
393 Toast.makeText(getContext(), R.string.tuner_toast,
394 Toast.LENGTH_LONG).show();
395 TunerService.setTunerEnabled(mContext, true);
396 }
397 startSettingsActivity();
398
399 });
400 } else {
401 startSettingsActivity();
402 }
403 } else if (v == mDateTimeGroup) {
404 Dependency.get(MetricsLogger.class).action(ACTION_QS_DATE,
405 mNextAlarm != null);
406 if (mNextAlarm != null) {
407 PendingIntent showIntent = mNextAlarm.getShowIntent();
408 mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
409 } else {
410 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
411 AlarmClock.ACTION_SHOW_ALARMS), 0);
412 }
413 }
414 }
415
416 private void startSettingsActivity() {
417 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
418 true /* dismissShade */);
419 }
420
421 @Override
422 public void setEmergencyCallsOnly(boolean show) {
423 boolean changed = show != mShowEmergencyCallsOnly;
424 if (changed) {
425 mShowEmergencyCallsOnly = show;
426 if (mExpanded) {
427 updateEverything();
428 }
429 }
430 }
431
432 @Override
433 public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
434 if (picture != null &&
Selim Cinek1a891a92017-12-04 17:41:27 +0100435 UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
Jason Monk826b6092017-08-29 11:30:52 -0400436 !(picture instanceof UserIconDrawable)) {
437 picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
Anthony Chen54daefe2017-04-07 17:19:54 -0700438 picture.setColorFilter(
439 Utils.getColorAttr(mContext, android.R.attr.colorForeground),
440 Mode.SRC_IN);
441 }
442 mMultiUserAvatar.setImageDrawable(picture);
443 }
444}