blob: cf549fa634f5f9ad64e3614c729bbc55eb5cd15c [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 android.content.Context;
22import android.content.Intent;
Amin Shaikh50c17892018-03-21 18:15:22 -040023import android.content.pm.UserInfo;
Amin Shaikh0e003312018-03-08 11:39:01 -050024import android.content.res.ColorStateList;
Anthony Chen54daefe2017-04-07 17:19:54 -070025import android.content.res.Configuration;
Anthony Chen54daefe2017-04-07 17:19:54 -070026import android.graphics.PorterDuff.Mode;
27import android.graphics.drawable.Drawable;
28import android.graphics.drawable.RippleDrawable;
29import android.os.UserManager;
Anthony Chen54daefe2017-04-07 17:19:54 -070030import android.support.annotation.Nullable;
31import android.support.annotation.VisibleForTesting;
Amin Shaikh0e003312018-03-08 11:39:01 -050032import android.text.TextUtils;
Anthony Chen54daefe2017-04-07 17:19:54 -070033import android.util.AttributeSet;
34import android.view.View;
35import android.view.View.OnClickListener;
36import android.widget.FrameLayout;
37import android.widget.ImageView;
Anthony Chen54daefe2017-04-07 17:19:54 -070038import android.widget.Toast;
39
40import com.android.internal.logging.MetricsLogger;
41import com.android.internal.logging.nano.MetricsProto;
Amin Shaikh0e003312018-03-08 11:39:01 -050042import com.android.keyguard.CarrierText;
Selim Cinek1a891a92017-12-04 17:41:27 +010043import com.android.keyguard.KeyguardUpdateMonitor;
Anthony Chen54daefe2017-04-07 17:19:54 -070044import com.android.settingslib.Utils;
Jason Monk826b6092017-08-29 11:30:52 -040045import com.android.settingslib.drawable.UserIconDrawable;
Amin Shaikh0e003312018-03-08 11:39:01 -050046import com.android.settingslib.graph.SignalDrawable;
Anthony Chen54daefe2017-04-07 17:19:54 -070047import com.android.systemui.Dependency;
Anthony Chen54daefe2017-04-07 17:19:54 -070048import com.android.systemui.R;
49import com.android.systemui.R.dimen;
Anthony Chen54daefe2017-04-07 17:19:54 -070050import com.android.systemui.plugins.ActivityStarter;
51import com.android.systemui.qs.TouchAnimator.Builder;
Anthony Chen54daefe2017-04-07 17:19:54 -070052import com.android.systemui.statusbar.phone.MultiUserSwitch;
53import com.android.systemui.statusbar.phone.SettingsButton;
54import com.android.systemui.statusbar.policy.DeviceProvisionedController;
55import com.android.systemui.statusbar.policy.NetworkController;
56import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
57import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
Anthony Chen54daefe2017-04-07 17:19:54 -070058import com.android.systemui.statusbar.policy.UserInfoController;
59import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
60import com.android.systemui.tuner.TunerService;
61
62public class QSFooterImpl extends FrameLayout implements QSFooter,
Amin Shaikhf09450b2018-04-06 17:32:45 -040063 OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback {
Evan Lairdeb38aa72018-02-02 11:10:30 -050064
Anthony Chen54daefe2017-04-07 17:19:54 -070065 private ActivityStarter mActivityStarter;
Anthony Chen54daefe2017-04-07 17:19:54 -070066 private UserInfoController mUserInfoController;
67 private SettingsButton mSettingsButton;
68 protected View mSettingsContainer;
Amin Shaikh0e003312018-03-08 11:39:01 -050069 private CarrierText mCarrierText;
Anthony Chen54daefe2017-04-07 17:19:54 -070070
Charles Hece2a7c02017-10-11 20:25:20 +010071 private boolean mQsDisabled;
Anthony Chen54daefe2017-04-07 17:19:54 -070072 private QSPanel mQsPanel;
73
74 private boolean mExpanded;
Anthony Chen54daefe2017-04-07 17:19:54 -070075
76 private boolean mListening;
Anthony Chen54daefe2017-04-07 17:19:54 -070077
78 private boolean mShowEmergencyCallsOnly;
Amin Shaikhacf322d2018-01-31 17:04:56 -050079 private View mDivider;
Anthony Chen54daefe2017-04-07 17:19:54 -070080 protected MultiUserSwitch mMultiUserSwitch;
81 private ImageView mMultiUserAvatar;
82
Evan Lairdeb38aa72018-02-02 11:10:30 -050083 protected TouchAnimator mFooterAnimator;
Anthony Chen54daefe2017-04-07 17:19:54 -070084 private float mExpansionAmount;
85
86 protected View mEdit;
Evan Lairdeb38aa72018-02-02 11:10:30 -050087 private TouchAnimator mSettingsCogAnimator;
Anthony Chen54daefe2017-04-07 17:19:54 -070088
Evan Laird00e43c42018-01-22 20:25:45 -050089 private View mActionsContainer;
Evan Lairdeb38aa72018-02-02 11:10:30 -050090 private View mDragHandle;
Amin Shaikh0e003312018-03-08 11:39:01 -050091 private View mMobileGroup;
92 private ImageView mMobileSignal;
93 private ImageView mMobileRoaming;
94 private final int mColorForeground;
95 private final CellSignalState mInfo = new CellSignalState();
Evan Laird00e43c42018-01-22 20:25:45 -050096
Anthony Chen54daefe2017-04-07 17:19:54 -070097 public QSFooterImpl(Context context, AttributeSet attrs) {
98 super(context, attrs);
Amin Shaikh0e003312018-03-08 11:39:01 -050099 mColorForeground = Utils.getColorAttr(context, android.R.attr.colorForeground);
Anthony Chen54daefe2017-04-07 17:19:54 -0700100 }
101
102 @Override
103 protected void onFinishInflate() {
104 super.onFinishInflate();
Amin Shaikhacf322d2018-01-31 17:04:56 -0500105 mDivider = findViewById(R.id.qs_footer_divider);
Anthony Chen54daefe2017-04-07 17:19:54 -0700106 mEdit = findViewById(android.R.id.edit);
107 mEdit.setOnClickListener(view ->
108 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
109 mQsPanel.showEdit(view)));
110
Anthony Chen54daefe2017-04-07 17:19:54 -0700111 mSettingsButton = findViewById(R.id.settings_button);
112 mSettingsContainer = findViewById(R.id.settings_button_container);
113 mSettingsButton.setOnClickListener(this);
114
Amin Shaikh0e003312018-03-08 11:39:01 -0500115 mMobileGroup = findViewById(R.id.mobile_combo);
116 mMobileSignal = findViewById(R.id.mobile_signal);
117 mMobileRoaming = findViewById(R.id.mobile_roaming);
Evan Laird39254d42018-01-18 16:05:30 -0500118 mCarrierText = findViewById(R.id.qs_carrier_text);
Amin Shaikh0e003312018-03-08 11:39:01 -0500119 mCarrierText.setDisplayFlags(
120 CarrierText.FLAG_HIDE_AIRPLANE_MODE | CarrierText.FLAG_HIDE_MISSING_SIM);
Evan Laird39254d42018-01-18 16:05:30 -0500121
Anthony Chen54daefe2017-04-07 17:19:54 -0700122 mMultiUserSwitch = findViewById(R.id.multi_user_switch);
123 mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
124
Evan Lairdeb38aa72018-02-02 11:10:30 -0500125 mDragHandle = findViewById(R.id.qs_drag_handle_view);
Evan Laird00e43c42018-01-22 20:25:45 -0500126 mActionsContainer = findViewById(R.id.qs_footer_actions_container);
127
Anthony Chen54daefe2017-04-07 17:19:54 -0700128 // RenderThread is doing more harm than good when touching the header (to expand quick
129 // settings), so disable it for this view
130 ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
Anthony Chen54daefe2017-04-07 17:19:54 -0700131
132 updateResources();
133
Anthony Chen54daefe2017-04-07 17:19:54 -0700134 mUserInfoController = Dependency.get(UserInfoController.class);
135 mActivityStarter = Dependency.get(ActivityStarter.class);
136 addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
137 oldBottom) -> updateAnimator(right - left));
138 }
139
140 private void updateAnimator(int width) {
141 int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
142 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
143 - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
144 int remaining = (width - numTiles * size) / (numTiles - 1);
145 int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
146
Evan Lairdeb38aa72018-02-02 11:10:30 -0500147 mSettingsCogAnimator = new Builder()
Jason Monk6dadd3e42017-09-01 09:36:19 -0400148 .addFloat(mSettingsContainer, "translationX",
149 isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
Anthony Chen54daefe2017-04-07 17:19:54 -0700150 .addFloat(mSettingsButton, "rotation", -120, 0)
151 .build();
Anthony Chen54daefe2017-04-07 17:19:54 -0700152
Anthony Chen54daefe2017-04-07 17:19:54 -0700153 setExpansion(mExpansionAmount);
154 }
155
156 @Override
157 protected void onConfigurationChanged(Configuration newConfig) {
158 super.onConfigurationChanged(newConfig);
159 updateResources();
160 }
161
162 @Override
163 public void onRtlPropertiesChanged(int layoutDirection) {
164 super.onRtlPropertiesChanged(layoutDirection);
165 updateResources();
166 }
167
168 private void updateResources() {
Evan Lairdeb38aa72018-02-02 11:10:30 -0500169 updateFooterAnimator();
Anthony Chen54daefe2017-04-07 17:19:54 -0700170 }
171
Evan Lairdeb38aa72018-02-02 11:10:30 -0500172 private void updateFooterAnimator() {
173 mFooterAnimator = createFooterAnimator();
Anthony Chen54daefe2017-04-07 17:19:54 -0700174 }
175
176 @Nullable
Evan Lairdeb38aa72018-02-02 11:10:30 -0500177 private TouchAnimator createFooterAnimator() {
Anthony Chen54daefe2017-04-07 17:19:54 -0700178 return new TouchAnimator.Builder()
Amin Shaikhacf322d2018-01-31 17:04:56 -0500179 .addFloat(mDivider, "alpha", 0, 1)
Amin Shaikhd620def2018-02-27 16:52:53 -0500180 .addFloat(mCarrierText, "alpha", 0, 0, 1)
Amin Shaikh0e003312018-03-08 11:39:01 -0500181 .addFloat(mMobileGroup, "alpha", 0, 1)
Evan Laird00e43c42018-01-22 20:25:45 -0500182 .addFloat(mActionsContainer, "alpha", 0, 1)
Amin Shaikhd620def2018-02-27 16:52:53 -0500183 .addFloat(mDragHandle, "alpha", 1, 0, 0)
Amin Shaikh92fac562018-02-16 15:08:16 -0500184 .setStartDelay(0.15f)
Anthony Chen54daefe2017-04-07 17:19:54 -0700185 .build();
186 }
187
188 @Override
189 public void setKeyguardShowing(boolean keyguardShowing) {
Anthony Chen54daefe2017-04-07 17:19:54 -0700190 setExpansion(mExpansionAmount);
191 }
192
193 @Override
194 public void setExpanded(boolean expanded) {
195 if (mExpanded == expanded) return;
196 mExpanded = expanded;
197 updateEverything();
198 }
199
200 @Override
Anthony Chen54daefe2017-04-07 17:19:54 -0700201 public void setExpansion(float headerExpansionFraction) {
202 mExpansionAmount = headerExpansionFraction;
Evan Lairdeb38aa72018-02-02 11:10:30 -0500203 if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
Anthony Chen54daefe2017-04-07 17:19:54 -0700204
Evan Lairdeb38aa72018-02-02 11:10:30 -0500205 if (mFooterAnimator != null) {
206 mFooterAnimator.setPosition(headerExpansionFraction);
Anthony Chen54daefe2017-04-07 17:19:54 -0700207 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700208 }
209
210 @Override
211 @VisibleForTesting
212 public void onDetachedFromWindow() {
213 setListening(false);
214 super.onDetachedFromWindow();
215 }
216
Anthony Chen54daefe2017-04-07 17:19:54 -0700217 @Override
218 public void setListening(boolean listening) {
219 if (listening == mListening) {
220 return;
221 }
222 mListening = listening;
223 updateListeners();
224 }
225
226 @Override
227 public View getExpandView() {
228 return findViewById(R.id.expand_indicator);
229 }
230
Charles Hece2a7c02017-10-11 20:25:20 +0100231 @Override
232 public void disable(int state1, int state2, boolean animate) {
233 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
234 if (disabled == mQsDisabled) return;
235 mQsDisabled = disabled;
236 updateEverything();
237 }
238
Anthony Chen54daefe2017-04-07 17:19:54 -0700239 public void updateEverything() {
240 post(() -> {
241 updateVisibilities();
242 setClickable(false);
243 });
244 }
245
246 private void updateVisibilities() {
Charles Hece2a7c02017-10-11 20:25:20 +0100247 mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
Anthony Chen54daefe2017-04-07 17:19:54 -0700248 mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
249 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
250 final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
Amin Shaikh50c17892018-03-21 18:15:22 -0400251 mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
Anthony Chen54daefe2017-04-07 17:19:54 -0700252 mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
253 }
254
Amin Shaikh50c17892018-03-21 18:15:22 -0400255 private boolean showUserSwitcher(boolean isDemo) {
256 if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
257 return false;
258 }
259 UserManager userManager = UserManager.get(mContext);
260 if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
261 return false;
262 }
263 int switchableUserCount = 0;
264 for (UserInfo user : userManager.getUsers(true)) {
265 if (user.supportsSwitchToByUser()) {
266 ++switchableUserCount;
267 if (switchableUserCount > 1) {
268 return true;
269 }
270 }
271 }
272 return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
273 }
274
Anthony Chen54daefe2017-04-07 17:19:54 -0700275 private void updateListeners() {
276 if (mListening) {
Anthony Chen54daefe2017-04-07 17:19:54 -0700277 mUserInfoController.addCallback(this);
278 if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
279 Dependency.get(NetworkController.class).addEmergencyListener(this);
280 Dependency.get(NetworkController.class).addCallback(this);
281 }
282 } else {
Anthony Chen54daefe2017-04-07 17:19:54 -0700283 mUserInfoController.removeCallback(this);
284 Dependency.get(NetworkController.class).removeEmergencyListener(this);
285 Dependency.get(NetworkController.class).removeCallback(this);
286 }
287 }
288
289 @Override
290 public void setQSPanel(final QSPanel qsPanel) {
291 mQsPanel = qsPanel;
292 if (mQsPanel != null) {
293 mMultiUserSwitch.setQsPanel(qsPanel);
294 }
295 }
296
297 @Override
298 public void onClick(View v) {
Evan Laird00e43c42018-01-22 20:25:45 -0500299 // Don't do anything until view are unhidden
300 if (!mExpanded) {
301 return;
302 }
303
Anthony Chen54daefe2017-04-07 17:19:54 -0700304 if (v == mSettingsButton) {
305 if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
306 // If user isn't setup just unlock the device and dump them back at SUW.
307 mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
308 return;
309 }
310 MetricsLogger.action(mContext,
311 mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
312 : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
313 if (mSettingsButton.isTunerClick()) {
314 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
315 if (TunerService.isTunerEnabled(mContext)) {
316 TunerService.showResetRequest(mContext, () -> {
317 // Relaunch settings so that the tuner disappears.
318 startSettingsActivity();
319 });
320 } else {
321 Toast.makeText(getContext(), R.string.tuner_toast,
322 Toast.LENGTH_LONG).show();
323 TunerService.setTunerEnabled(mContext, true);
324 }
325 startSettingsActivity();
326
327 });
328 } else {
329 startSettingsActivity();
330 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700331 }
332 }
333
334 private void startSettingsActivity() {
335 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
336 true /* dismissShade */);
337 }
338
339 @Override
340 public void setEmergencyCallsOnly(boolean show) {
341 boolean changed = show != mShowEmergencyCallsOnly;
342 if (changed) {
343 mShowEmergencyCallsOnly = show;
344 if (mExpanded) {
345 updateEverything();
346 }
347 }
348 }
349
350 @Override
351 public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
352 if (picture != null &&
Selim Cinek1a891a92017-12-04 17:41:27 +0100353 UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
Jason Monk826b6092017-08-29 11:30:52 -0400354 !(picture instanceof UserIconDrawable)) {
355 picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
Anthony Chen54daefe2017-04-07 17:19:54 -0700356 picture.setColorFilter(
357 Utils.getColorAttr(mContext, android.R.attr.colorForeground),
358 Mode.SRC_IN);
359 }
360 mMultiUserAvatar.setImageDrawable(picture);
361 }
Amin Shaikh0e003312018-03-08 11:39:01 -0500362
363 private void handleUpdateState() {
364 mMobileGroup.setVisibility(mInfo.visible ? View.VISIBLE : View.GONE);
365 if (mInfo.visible) {
366 mMobileRoaming.setVisibility(mInfo.roaming ? View.VISIBLE : View.GONE);
367 mMobileRoaming.setImageTintList(ColorStateList.valueOf(mColorForeground));
368 SignalDrawable d = new SignalDrawable(mContext);
369 d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
370 mMobileSignal.setImageDrawable(d);
371 mMobileSignal.setImageLevel(mInfo.mobileSignalIconId);
372
373 StringBuilder contentDescription = new StringBuilder();
374 if (mInfo.contentDescription != null) {
375 contentDescription.append(mInfo.contentDescription).append(", ");
376 }
377 if (mInfo.roaming) {
378 contentDescription
379 .append(mContext.getString(R.string.data_connection_roaming))
380 .append(", ");
381 }
382 // TODO: show mobile data off/no internet text for 5 seconds before carrier text
383 if (TextUtils.equals(mInfo.typeContentDescription,
384 mContext.getString(R.string.data_connection_no_internet))
385 || TextUtils.equals(mInfo.typeContentDescription,
386 mContext.getString(R.string.cell_data_off))) {
387 contentDescription.append(mInfo.typeContentDescription);
388 }
389 mMobileSignal.setContentDescription(contentDescription);
390 }
391 }
392
393 @Override
394 public void setMobileDataIndicators(NetworkController.IconState statusIcon,
395 NetworkController.IconState qsIcon, int statusType,
396 int qsType, boolean activityIn, boolean activityOut,
397 String typeContentDescription,
398 String description, boolean isWide, int subId, boolean roaming) {
399 mInfo.visible = statusIcon.visible;
400 mInfo.mobileSignalIconId = statusIcon.icon;
401 mInfo.contentDescription = statusIcon.contentDescription;
402 mInfo.typeContentDescription = typeContentDescription;
403 mInfo.roaming = roaming;
404 handleUpdateState();
405 }
406
407 @Override
408 public void setNoSims(boolean hasNoSims, boolean simDetected) {
409 if (hasNoSims) {
410 mInfo.visible = false;
411 }
412 handleUpdateState();
413 }
414
415 private final class CellSignalState {
416 boolean visible;
417 int mobileSignalIconId;
418 public String contentDescription;
419 String typeContentDescription;
420 boolean roaming;
421 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700422}