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