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