blob: 0fa659748f5d6c776e7edd01dfa83fed8ad8bf52 [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;
Charles Hece2a7c02017-10-11 20:25:20 +010050import com.android.systemui.SysUiServiceProvider;
Anthony Chen54daefe2017-04-07 17:19:54 -070051import com.android.systemui.plugins.ActivityStarter;
52import com.android.systemui.qs.TouchAnimator.Builder;
Charles Hece2a7c02017-10-11 20:25:20 +010053import com.android.systemui.statusbar.CommandQueue;
Anthony Chen54daefe2017-04-07 17:19:54 -070054import com.android.systemui.statusbar.phone.MultiUserSwitch;
55import com.android.systemui.statusbar.phone.SettingsButton;
56import com.android.systemui.statusbar.policy.DeviceProvisionedController;
57import com.android.systemui.statusbar.policy.NetworkController;
58import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
59import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
Anthony Chen54daefe2017-04-07 17:19:54 -070060import com.android.systemui.statusbar.policy.UserInfoController;
61import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
62import com.android.systemui.tuner.TunerService;
63
64public class QSFooterImpl extends FrameLayout implements QSFooter,
Evan Laird2cf56822017-12-18 11:22:39 -050065 OnClickListener, OnUserInfoChangedListener, EmergencyListener,
Charles Hece2a7c02017-10-11 20:25:20 +010066 SignalCallback, CommandQueue.Callbacks {
Evan Lairdeb38aa72018-02-02 11:10:30 -050067
Anthony Chen54daefe2017-04-07 17:19:54 -070068 private ActivityStarter mActivityStarter;
Anthony Chen54daefe2017-04-07 17:19:54 -070069 private UserInfoController mUserInfoController;
70 private SettingsButton mSettingsButton;
71 protected View mSettingsContainer;
Amin Shaikh0e003312018-03-08 11:39:01 -050072 private CarrierText mCarrierText;
Anthony Chen54daefe2017-04-07 17:19:54 -070073
Charles Hece2a7c02017-10-11 20:25:20 +010074 private boolean mQsDisabled;
Anthony Chen54daefe2017-04-07 17:19:54 -070075 private QSPanel mQsPanel;
76
77 private boolean mExpanded;
Anthony Chen54daefe2017-04-07 17:19:54 -070078
79 private boolean mListening;
Anthony Chen54daefe2017-04-07 17:19:54 -070080
81 private boolean mShowEmergencyCallsOnly;
Amin Shaikhacf322d2018-01-31 17:04:56 -050082 private View mDivider;
Anthony Chen54daefe2017-04-07 17:19:54 -070083 protected MultiUserSwitch mMultiUserSwitch;
84 private ImageView mMultiUserAvatar;
85
Evan Lairdeb38aa72018-02-02 11:10:30 -050086 protected TouchAnimator mFooterAnimator;
Anthony Chen54daefe2017-04-07 17:19:54 -070087 private float mExpansionAmount;
88
89 protected View mEdit;
Evan Lairdeb38aa72018-02-02 11:10:30 -050090 private TouchAnimator mSettingsCogAnimator;
Anthony Chen54daefe2017-04-07 17:19:54 -070091
Evan Laird00e43c42018-01-22 20:25:45 -050092 private View mActionsContainer;
Evan Lairdeb38aa72018-02-02 11:10:30 -050093 private View mDragHandle;
Amin Shaikh0e003312018-03-08 11:39:01 -050094 private View mMobileGroup;
95 private ImageView mMobileSignal;
96 private ImageView mMobileRoaming;
97 private final int mColorForeground;
98 private final CellSignalState mInfo = new CellSignalState();
Evan Laird00e43c42018-01-22 20:25:45 -050099
Anthony Chen54daefe2017-04-07 17:19:54 -0700100 public QSFooterImpl(Context context, AttributeSet attrs) {
101 super(context, attrs);
Amin Shaikh0e003312018-03-08 11:39:01 -0500102 mColorForeground = Utils.getColorAttr(context, android.R.attr.colorForeground);
Anthony Chen54daefe2017-04-07 17:19:54 -0700103 }
104
105 @Override
106 protected void onFinishInflate() {
107 super.onFinishInflate();
Amin Shaikhacf322d2018-01-31 17:04:56 -0500108 mDivider = findViewById(R.id.qs_footer_divider);
Anthony Chen54daefe2017-04-07 17:19:54 -0700109 mEdit = findViewById(android.R.id.edit);
110 mEdit.setOnClickListener(view ->
111 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
112 mQsPanel.showEdit(view)));
113
Anthony Chen54daefe2017-04-07 17:19:54 -0700114 mSettingsButton = findViewById(R.id.settings_button);
115 mSettingsContainer = findViewById(R.id.settings_button_container);
116 mSettingsButton.setOnClickListener(this);
117
Amin Shaikh0e003312018-03-08 11:39:01 -0500118 mMobileGroup = findViewById(R.id.mobile_combo);
119 mMobileSignal = findViewById(R.id.mobile_signal);
120 mMobileRoaming = findViewById(R.id.mobile_roaming);
Evan Laird39254d42018-01-18 16:05:30 -0500121 mCarrierText = findViewById(R.id.qs_carrier_text);
Amin Shaikh0e003312018-03-08 11:39:01 -0500122 mCarrierText.setDisplayFlags(
123 CarrierText.FLAG_HIDE_AIRPLANE_MODE | CarrierText.FLAG_HIDE_MISSING_SIM);
Evan Laird39254d42018-01-18 16:05:30 -0500124
Anthony Chen54daefe2017-04-07 17:19:54 -0700125 mMultiUserSwitch = findViewById(R.id.multi_user_switch);
126 mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
127
Evan Lairdeb38aa72018-02-02 11:10:30 -0500128 mDragHandle = findViewById(R.id.qs_drag_handle_view);
Evan Laird00e43c42018-01-22 20:25:45 -0500129 mActionsContainer = findViewById(R.id.qs_footer_actions_container);
130
Anthony Chen54daefe2017-04-07 17:19:54 -0700131 // RenderThread is doing more harm than good when touching the header (to expand quick
132 // settings), so disable it for this view
133 ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
Anthony Chen54daefe2017-04-07 17:19:54 -0700134
135 updateResources();
136
Anthony Chen54daefe2017-04-07 17:19:54 -0700137 mUserInfoController = Dependency.get(UserInfoController.class);
138 mActivityStarter = Dependency.get(ActivityStarter.class);
139 addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
140 oldBottom) -> updateAnimator(right - left));
141 }
142
143 private void updateAnimator(int width) {
144 int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
145 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
146 - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
147 int remaining = (width - numTiles * size) / (numTiles - 1);
148 int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
149
Evan Lairdeb38aa72018-02-02 11:10:30 -0500150 mSettingsCogAnimator = new Builder()
Jason Monk6dadd3e42017-09-01 09:36:19 -0400151 .addFloat(mSettingsContainer, "translationX",
152 isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
Anthony Chen54daefe2017-04-07 17:19:54 -0700153 .addFloat(mSettingsButton, "rotation", -120, 0)
154 .build();
Anthony Chen54daefe2017-04-07 17:19:54 -0700155
Anthony Chen54daefe2017-04-07 17:19:54 -0700156 setExpansion(mExpansionAmount);
157 }
158
159 @Override
160 protected void onConfigurationChanged(Configuration newConfig) {
161 super.onConfigurationChanged(newConfig);
162 updateResources();
163 }
164
165 @Override
166 public void onRtlPropertiesChanged(int layoutDirection) {
167 super.onRtlPropertiesChanged(layoutDirection);
168 updateResources();
169 }
170
171 private void updateResources() {
Evan Lairdeb38aa72018-02-02 11:10:30 -0500172 updateFooterAnimator();
Anthony Chen54daefe2017-04-07 17:19:54 -0700173 }
174
Evan Lairdeb38aa72018-02-02 11:10:30 -0500175 private void updateFooterAnimator() {
176 mFooterAnimator = createFooterAnimator();
Anthony Chen54daefe2017-04-07 17:19:54 -0700177 }
178
179 @Nullable
Evan Lairdeb38aa72018-02-02 11:10:30 -0500180 private TouchAnimator createFooterAnimator() {
Anthony Chen54daefe2017-04-07 17:19:54 -0700181 return new TouchAnimator.Builder()
Amin Shaikhacf322d2018-01-31 17:04:56 -0500182 .addFloat(mDivider, "alpha", 0, 1)
Amin Shaikhd620def2018-02-27 16:52:53 -0500183 .addFloat(mCarrierText, "alpha", 0, 0, 1)
Amin Shaikh0e003312018-03-08 11:39:01 -0500184 .addFloat(mMobileGroup, "alpha", 0, 1)
Evan Laird00e43c42018-01-22 20:25:45 -0500185 .addFloat(mActionsContainer, "alpha", 0, 1)
Amin Shaikhd620def2018-02-27 16:52:53 -0500186 .addFloat(mDragHandle, "alpha", 1, 0, 0)
Amin Shaikh92fac562018-02-16 15:08:16 -0500187 .setStartDelay(0.15f)
Anthony Chen54daefe2017-04-07 17:19:54 -0700188 .build();
189 }
190
191 @Override
192 public void setKeyguardShowing(boolean keyguardShowing) {
Anthony Chen54daefe2017-04-07 17:19:54 -0700193 setExpansion(mExpansionAmount);
194 }
195
196 @Override
197 public void setExpanded(boolean expanded) {
198 if (mExpanded == expanded) return;
199 mExpanded = expanded;
200 updateEverything();
201 }
202
203 @Override
Anthony Chen54daefe2017-04-07 17:19:54 -0700204 public void setExpansion(float headerExpansionFraction) {
205 mExpansionAmount = headerExpansionFraction;
Evan Lairdeb38aa72018-02-02 11:10:30 -0500206 if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
Anthony Chen54daefe2017-04-07 17:19:54 -0700207
Evan Lairdeb38aa72018-02-02 11:10:30 -0500208 if (mFooterAnimator != null) {
209 mFooterAnimator.setPosition(headerExpansionFraction);
Anthony Chen54daefe2017-04-07 17:19:54 -0700210 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700211 }
212
213 @Override
Charles Hece2a7c02017-10-11 20:25:20 +0100214 public void onAttachedToWindow() {
215 super.onAttachedToWindow();
216 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
217 }
218
219 @Override
Anthony Chen54daefe2017-04-07 17:19:54 -0700220 @VisibleForTesting
221 public void onDetachedFromWindow() {
222 setListening(false);
Charles Hece2a7c02017-10-11 20:25:20 +0100223 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
Anthony Chen54daefe2017-04-07 17:19:54 -0700224 super.onDetachedFromWindow();
225 }
226
Anthony Chen54daefe2017-04-07 17:19:54 -0700227 @Override
228 public void setListening(boolean listening) {
229 if (listening == mListening) {
230 return;
231 }
232 mListening = listening;
233 updateListeners();
234 }
235
236 @Override
237 public View getExpandView() {
238 return findViewById(R.id.expand_indicator);
239 }
240
Charles Hece2a7c02017-10-11 20:25:20 +0100241 @Override
242 public void disable(int state1, int state2, boolean animate) {
243 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
244 if (disabled == mQsDisabled) return;
245 mQsDisabled = disabled;
246 updateEverything();
247 }
248
Anthony Chen54daefe2017-04-07 17:19:54 -0700249 public void updateEverything() {
250 post(() -> {
251 updateVisibilities();
252 setClickable(false);
253 });
254 }
255
256 private void updateVisibilities() {
Charles Hece2a7c02017-10-11 20:25:20 +0100257 mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
Anthony Chen54daefe2017-04-07 17:19:54 -0700258 mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
259 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
260 final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
Amin Shaikh50c17892018-03-21 18:15:22 -0400261 mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
Anthony Chen54daefe2017-04-07 17:19:54 -0700262 mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
263 }
264
Amin Shaikh50c17892018-03-21 18:15:22 -0400265 private boolean showUserSwitcher(boolean isDemo) {
266 if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
267 return false;
268 }
269 UserManager userManager = UserManager.get(mContext);
270 if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
271 return false;
272 }
273 int switchableUserCount = 0;
274 for (UserInfo user : userManager.getUsers(true)) {
275 if (user.supportsSwitchToByUser()) {
276 ++switchableUserCount;
277 if (switchableUserCount > 1) {
278 return true;
279 }
280 }
281 }
282 return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
283 }
284
Anthony Chen54daefe2017-04-07 17:19:54 -0700285 private void updateListeners() {
286 if (mListening) {
Anthony Chen54daefe2017-04-07 17:19:54 -0700287 mUserInfoController.addCallback(this);
288 if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
289 Dependency.get(NetworkController.class).addEmergencyListener(this);
290 Dependency.get(NetworkController.class).addCallback(this);
291 }
292 } else {
Anthony Chen54daefe2017-04-07 17:19:54 -0700293 mUserInfoController.removeCallback(this);
294 Dependency.get(NetworkController.class).removeEmergencyListener(this);
295 Dependency.get(NetworkController.class).removeCallback(this);
296 }
297 }
298
299 @Override
300 public void setQSPanel(final QSPanel qsPanel) {
301 mQsPanel = qsPanel;
302 if (mQsPanel != null) {
303 mMultiUserSwitch.setQsPanel(qsPanel);
304 }
305 }
306
307 @Override
308 public void onClick(View v) {
Evan Laird00e43c42018-01-22 20:25:45 -0500309 // Don't do anything until view are unhidden
310 if (!mExpanded) {
311 return;
312 }
313
Anthony Chen54daefe2017-04-07 17:19:54 -0700314 if (v == mSettingsButton) {
315 if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
316 // If user isn't setup just unlock the device and dump them back at SUW.
317 mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
318 return;
319 }
320 MetricsLogger.action(mContext,
321 mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
322 : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
323 if (mSettingsButton.isTunerClick()) {
324 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
325 if (TunerService.isTunerEnabled(mContext)) {
326 TunerService.showResetRequest(mContext, () -> {
327 // Relaunch settings so that the tuner disappears.
328 startSettingsActivity();
329 });
330 } else {
331 Toast.makeText(getContext(), R.string.tuner_toast,
332 Toast.LENGTH_LONG).show();
333 TunerService.setTunerEnabled(mContext, true);
334 }
335 startSettingsActivity();
336
337 });
338 } else {
339 startSettingsActivity();
340 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700341 }
342 }
343
344 private void startSettingsActivity() {
345 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
346 true /* dismissShade */);
347 }
348
349 @Override
350 public void setEmergencyCallsOnly(boolean show) {
351 boolean changed = show != mShowEmergencyCallsOnly;
352 if (changed) {
353 mShowEmergencyCallsOnly = show;
354 if (mExpanded) {
355 updateEverything();
356 }
357 }
358 }
359
360 @Override
361 public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
362 if (picture != null &&
Selim Cinek1a891a92017-12-04 17:41:27 +0100363 UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
Jason Monk826b6092017-08-29 11:30:52 -0400364 !(picture instanceof UserIconDrawable)) {
365 picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
Anthony Chen54daefe2017-04-07 17:19:54 -0700366 picture.setColorFilter(
367 Utils.getColorAttr(mContext, android.R.attr.colorForeground),
368 Mode.SRC_IN);
369 }
370 mMultiUserAvatar.setImageDrawable(picture);
371 }
Amin Shaikh0e003312018-03-08 11:39:01 -0500372
373 private void handleUpdateState() {
374 mMobileGroup.setVisibility(mInfo.visible ? View.VISIBLE : View.GONE);
375 if (mInfo.visible) {
376 mMobileRoaming.setVisibility(mInfo.roaming ? View.VISIBLE : View.GONE);
377 mMobileRoaming.setImageTintList(ColorStateList.valueOf(mColorForeground));
378 SignalDrawable d = new SignalDrawable(mContext);
379 d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
380 mMobileSignal.setImageDrawable(d);
381 mMobileSignal.setImageLevel(mInfo.mobileSignalIconId);
382
383 StringBuilder contentDescription = new StringBuilder();
384 if (mInfo.contentDescription != null) {
385 contentDescription.append(mInfo.contentDescription).append(", ");
386 }
387 if (mInfo.roaming) {
388 contentDescription
389 .append(mContext.getString(R.string.data_connection_roaming))
390 .append(", ");
391 }
392 // TODO: show mobile data off/no internet text for 5 seconds before carrier text
393 if (TextUtils.equals(mInfo.typeContentDescription,
394 mContext.getString(R.string.data_connection_no_internet))
395 || TextUtils.equals(mInfo.typeContentDescription,
396 mContext.getString(R.string.cell_data_off))) {
397 contentDescription.append(mInfo.typeContentDescription);
398 }
399 mMobileSignal.setContentDescription(contentDescription);
400 }
401 }
402
403 @Override
404 public void setMobileDataIndicators(NetworkController.IconState statusIcon,
405 NetworkController.IconState qsIcon, int statusType,
406 int qsType, boolean activityIn, boolean activityOut,
407 String typeContentDescription,
408 String description, boolean isWide, int subId, boolean roaming) {
409 mInfo.visible = statusIcon.visible;
410 mInfo.mobileSignalIconId = statusIcon.icon;
411 mInfo.contentDescription = statusIcon.contentDescription;
412 mInfo.typeContentDescription = typeContentDescription;
413 mInfo.roaming = roaming;
414 handleUpdateState();
415 }
416
417 @Override
418 public void setNoSims(boolean hasNoSims, boolean simDetected) {
419 if (hasNoSims) {
420 mInfo.visible = false;
421 }
422 handleUpdateState();
423 }
424
425 private final class CellSignalState {
426 boolean visible;
427 int mobileSignalIconId;
428 public String contentDescription;
429 String typeContentDescription;
430 boolean roaming;
431 }
Anthony Chen54daefe2017-04-07 17:19:54 -0700432}