blob: 394c3225bceb9e17305b47061f305fa432575f64 [file] [log] [blame]
Jason Monkbd6dbb02015-09-03 15:46:25 -04001/*
2 * Copyright (C) 2015 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 */
16package com.android.systemui.qs.customize;
17
Jason Monkb9c00192015-10-07 11:45:33 -040018import android.animation.Animator;
Jason Monk62b63a02016-02-02 15:15:31 -050019import android.animation.Animator.AnimatorListener;
Jason Monk77a2c5f2016-02-22 15:18:36 -050020import android.animation.AnimatorListenerAdapter;
Jason Monkbd6dbb02015-09-03 15:46:25 -040021import android.content.Context;
Jason Monk931f5462016-05-25 11:19:04 -040022import android.content.res.Configuration;
Amin Shaikh9a6fa082018-03-29 17:27:26 -040023import android.graphics.Point;
Selim Cinek707e2072017-06-30 18:32:40 +020024import android.os.Bundle;
Jason Monk62b63a02016-02-02 15:15:31 -050025import android.support.v7.widget.DefaultItemAnimator;
26import android.support.v7.widget.GridLayoutManager;
27import android.support.v7.widget.RecyclerView;
Jason Monkbd6dbb02015-09-03 15:46:25 -040028import android.util.AttributeSet;
Jason Monkb53b6c52016-02-24 17:25:49 -050029import android.util.TypedValue;
Jason Monk5db8a412015-10-21 15:16:23 -070030import android.view.ContextThemeWrapper;
Jason Monka8321e62016-03-11 08:56:25 -050031import android.view.LayoutInflater;
Jason Monkb53b6c52016-02-24 17:25:49 -050032import android.view.Menu;
33import android.view.MenuItem;
Jason Monk5db8a412015-10-21 15:16:23 -070034import android.view.View;
Jason Monkbd6dbb02015-09-03 15:46:25 -040035import android.widget.LinearLayout;
Jason Monkb53b6c52016-02-24 17:25:49 -050036import android.widget.Toolbar;
37import android.widget.Toolbar.OnMenuItemClickListener;
Jason Monk46767b72016-08-18 10:58:04 -040038
Jason Monk96defbe2016-03-29 16:51:03 -040039import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010040import com.android.internal.logging.nano.MetricsProto;
Amin Shaikh9a6fa082018-03-29 17:27:26 -040041import com.android.settingslib.Utils;
Jason Monk9c7844c2017-01-18 15:21:53 -050042import com.android.systemui.Dependency;
Jason Monkbd6dbb02015-09-03 15:46:25 -040043import com.android.systemui.R;
Jason Monk0ceef212016-11-02 14:05:23 -040044import com.android.systemui.plugins.qs.QS;
Jason Monk702e2eb2017-03-03 16:53:44 -050045import com.android.systemui.plugins.qs.QSTile;
Jason Monk78a3d602017-03-16 12:12:57 -040046import com.android.systemui.qs.QSDetailClipper;
Jason Monke5b770e2017-03-03 21:49:29 -050047import com.android.systemui.qs.QSTileHost;
Jason Monk73112d32017-09-06 16:53:53 -040048import com.android.systemui.statusbar.phone.LightBarController;
Jason Monk78a3d602017-03-16 12:12:57 -040049import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
Jason Monk9c7844c2017-01-18 15:21:53 -050050import com.android.systemui.statusbar.policy.KeyguardMonitor;
Jason Monk181d3fe2016-06-06 16:18:50 -040051import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
Jason Monkbd6dbb02015-09-03 15:46:25 -040052
53import java.util.ArrayList;
Jason Monk62b63a02016-02-02 15:15:31 -050054import java.util.List;
Jason Monkbd6dbb02015-09-03 15:46:25 -040055
56/**
57 * Allows full-screen customization of QS, through show() and hide().
58 *
59 * This adds itself to the status bar window, so it can appear on top of quick settings and
60 * *someday* do fancy animations to get into/out of it.
61 */
Jason Monkb53b6c52016-02-24 17:25:49 -050062public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener {
63
64 private static final int MENU_RESET = Menu.FIRST;
Selim Cinek707e2072017-06-30 18:32:40 +020065 private static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
Jason Monkbd6dbb02015-09-03 15:46:25 -040066
Jason Monkb9c00192015-10-07 11:45:33 -040067 private final QSDetailClipper mClipper;
Jason Monk73112d32017-09-06 16:53:53 -040068 private final LightBarController mLightBarController;
Amin Shaikhc5b1bc82018-03-21 08:13:47 -040069 private final TileQueryHelper mTileQueryHelper;
Jason Monkbd6dbb02015-09-03 15:46:25 -040070
Jason Monkbd6dbb02015-09-03 15:46:25 -040071 private boolean isShown;
Jason Monkbbadff82015-11-06 15:47:26 -050072 private QSTileHost mHost;
Jason Monk62b63a02016-02-02 15:15:31 -050073 private RecyclerView mRecyclerView;
74 private TileAdapter mTileAdapter;
Jason Monkb53b6c52016-02-24 17:25:49 -050075 private Toolbar mToolbar;
Jason Monk8b9d67f2016-03-02 08:59:08 -050076 private boolean mCustomizing;
77 private NotificationsQuickSettingsContainer mNotifQsContainer;
Jason Monk0ceef212016-11-02 14:05:23 -040078 private QS mQs;
Jason Monke5b770e2017-03-03 21:49:29 -050079 private int mX;
80 private int mY;
Jason Monk78a3d602017-03-16 12:12:57 -040081 private boolean mOpening;
Jason Monk73112d32017-09-06 16:53:53 -040082 private boolean mIsShowingNavBackdrop;
Jason Monkbd6dbb02015-09-03 15:46:25 -040083
84 public QSCustomizer(Context context, AttributeSet attrs) {
Jason Monka8321e62016-03-11 08:56:25 -050085 super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
Jason Monkbd6dbb02015-09-03 15:46:25 -040086
Jason Monka8321e62016-03-11 08:56:25 -050087 LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
Amin Shaikh9a6fa082018-03-29 17:27:26 -040088 mClipper = new QSDetailClipper(findViewById(R.id.customize_container));
Alan Viverette51efddb2017-04-05 10:00:01 -040089 mToolbar = findViewById(com.android.internal.R.id.action_bar);
Jason Monkb53b6c52016-02-24 17:25:49 -050090 TypedValue value = new TypedValue();
91 mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
92 mToolbar.setNavigationIcon(
Jason Monk04fd2492016-02-29 14:29:26 -050093 getResources().getDrawable(value.resourceId, mContext.getTheme()));
Jason Monkb53b6c52016-02-24 17:25:49 -050094 mToolbar.setNavigationOnClickListener(new OnClickListener() {
95 @Override
96 public void onClick(View v) {
Jason Monkb53b6c52016-02-24 17:25:49 -050097 hide((int) v.getX() + v.getWidth() / 2, (int) v.getY() + v.getHeight() / 2);
98 }
99 });
100 mToolbar.setOnMenuItemClickListener(this);
101 mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
102 mContext.getString(com.android.internal.R.string.reset));
Jason Monkf923db52016-03-02 14:11:14 -0500103 mToolbar.setTitle(R.string.qs_edit);
Amin Shaikh9a6fa082018-03-29 17:27:26 -0400104 int accentColor = Utils.getColorAttr(context, android.R.attr.colorAccent);
105 mToolbar.setTitleTextColor(accentColor);
106 mToolbar.getNavigationIcon().setTint(accentColor);
107 mToolbar.getOverflowIcon().setTint(accentColor);
Alan Viverette51efddb2017-04-05 10:00:01 -0400108 mRecyclerView = findViewById(android.R.id.list);
Jason Monk62b63a02016-02-02 15:15:31 -0500109 mTileAdapter = new TileAdapter(getContext());
Amin Shaikhc5b1bc82018-03-21 08:13:47 -0400110 mTileQueryHelper = new TileQueryHelper(context, mTileAdapter);
Jason Monk62b63a02016-02-02 15:15:31 -0500111 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb53b6c52016-02-24 17:25:49 -0500112 mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
Jason Monk62b63a02016-02-02 15:15:31 -0500113 GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
114 layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
115 mRecyclerView.setLayoutManager(layout);
116 mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
117 DefaultItemAnimator animator = new DefaultItemAnimator();
118 animator.setMoveDuration(TileAdapter.MOVE_DURATION);
119 mRecyclerView.setItemAnimator(animator);
Jason Monk73112d32017-09-06 16:53:53 -0400120 mLightBarController = Dependency.get(LightBarController.class);
Selim Cinek707e2072017-06-30 18:32:40 +0200121 updateNavBackDrop(getResources().getConfiguration());
Jason Monkbd6dbb02015-09-03 15:46:25 -0400122 }
123
Jason Monk931f5462016-05-25 11:19:04 -0400124 @Override
125 protected void onConfigurationChanged(Configuration newConfig) {
126 super.onConfigurationChanged(newConfig);
Selim Cinek707e2072017-06-30 18:32:40 +0200127 updateNavBackDrop(newConfig);
128 }
129
130 private void updateNavBackDrop(Configuration newConfig) {
Jason Monk931f5462016-05-25 11:19:04 -0400131 View navBackdrop = findViewById(R.id.nav_bar_background);
Jason Monk73112d32017-09-06 16:53:53 -0400132 mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600
133 || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
Jason Monk931f5462016-05-25 11:19:04 -0400134 if (navBackdrop != null) {
Jason Monk73112d32017-09-06 16:53:53 -0400135 navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE);
Jason Monk931f5462016-05-25 11:19:04 -0400136 }
Jason Monk73112d32017-09-06 16:53:53 -0400137 updateNavColors();
138 }
139
140 private void updateNavColors() {
141 mLightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
Jason Monk931f5462016-05-25 11:19:04 -0400142 }
143
Jason Monka8321e62016-03-11 08:56:25 -0500144 public void setHost(QSTileHost host) {
145 mHost = host;
Jason Monk63554ed2016-04-29 14:58:29 -0400146 mTileAdapter.setHost(host);
Jason Monka8321e62016-03-11 08:56:25 -0500147 }
148
149 public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
150 mNotifQsContainer = notificationsQsContainer;
151 }
152
Jason Monk0ceef212016-11-02 14:05:23 -0400153 public void setQs(QS qs) {
154 mQs = qs;
Jason Monka8321e62016-03-11 08:56:25 -0500155 }
156
Jason Monkb9c00192015-10-07 11:45:33 -0400157 public void show(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500158 if (!isShown) {
Jason Monke5b770e2017-03-03 21:49:29 -0500159 mX = x;
160 mY = y;
Jason Monk96defbe2016-03-29 16:51:03 -0400161 MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500162 isShown = true;
Jason Monk78a3d602017-03-16 12:12:57 -0400163 mOpening = true;
Jason Monk8c7e47f2016-02-17 10:39:31 -0500164 setTileSpecs();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500165 setVisibility(View.VISIBLE);
166 mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
Evan Laird6b284732017-02-28 17:27:04 -0500167 queryTiles();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500168 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400169 mNotifQsContainer.setCustomizerShowing(true);
Jason Monk9c7844c2017-01-18 15:21:53 -0500170 Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
Jason Monk73112d32017-09-06 16:53:53 -0400171 updateNavColors();
Jason Monk8c7e47f2016-02-17 10:39:31 -0500172 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400173 }
174
Selim Cinek707e2072017-06-30 18:32:40 +0200175
176 public void showImmediately() {
177 if (!isShown) {
178 setVisibility(VISIBLE);
179 mClipper.showBackground();
180 isShown = true;
181 setTileSpecs();
182 setCustomizing(true);
183 queryTiles();
184 mNotifQsContainer.setCustomizerAnimating(false);
185 mNotifQsContainer.setCustomizerShowing(true);
186 Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
Jason Monk73112d32017-09-06 16:53:53 -0400187 updateNavColors();
Selim Cinek707e2072017-06-30 18:32:40 +0200188 }
189 }
190
Evan Laird6b284732017-02-28 17:27:04 -0500191 private void queryTiles() {
Amin Shaikhc5b1bc82018-03-21 08:13:47 -0400192 mTileQueryHelper.queryTiles(mHost);
Evan Laird6b284732017-02-28 17:27:04 -0500193 }
194
Jason Monkb9c00192015-10-07 11:45:33 -0400195 public void hide(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500196 if (isShown) {
Jason Monk96defbe2016-03-29 16:51:03 -0400197 MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500198 isShown = false;
Jason Monkdf8cbc32016-03-16 16:10:56 -0400199 mToolbar.dismissPopupMenus();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500200 setCustomizing(false);
Jason Monk04fd2492016-02-29 14:29:26 -0500201 save();
Jason Monke5b770e2017-03-03 21:49:29 -0500202 mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500203 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400204 mNotifQsContainer.setCustomizerShowing(false);
Jason Monk9c7844c2017-01-18 15:21:53 -0500205 Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
Jason Monk73112d32017-09-06 16:53:53 -0400206 updateNavColors();
Jason Monk8c7e47f2016-02-17 10:39:31 -0500207 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400208 }
209
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900210 public boolean isShown() {
211 return isShown;
212 }
213
Jason Monk8b9d67f2016-03-02 08:59:08 -0500214 private void setCustomizing(boolean customizing) {
215 mCustomizing = customizing;
Jason Monk0ceef212016-11-02 14:05:23 -0400216 mQs.notifyCustomizeChanged();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500217 }
218
Jason Monkbd6dbb02015-09-03 15:46:25 -0400219 public boolean isCustomizing() {
Amin Shaikh1420e7e2018-04-11 11:12:33 -0400220 return mCustomizing || mOpening;
Jason Monkbd6dbb02015-09-03 15:46:25 -0400221 }
222
Jason Monkb53b6c52016-02-24 17:25:49 -0500223 @Override
224 public boolean onMenuItemClick(MenuItem item) {
225 switch (item.getItemId()) {
226 case MENU_RESET:
Jason Monk96defbe2016-03-29 16:51:03 -0400227 MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
Jason Monkb53b6c52016-02-24 17:25:49 -0500228 reset();
229 break;
230 }
231 return false;
232 }
233
Jason Monkbd6dbb02015-09-03 15:46:25 -0400234 private void reset() {
235 ArrayList<String> tiles = new ArrayList<>();
Jason Monk9d02a432016-01-20 16:33:46 -0500236 String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
237 for (String tile : defTiles.split(",")) {
Jason Monkbd6dbb02015-09-03 15:46:25 -0400238 tiles.add(tile);
239 }
Jason Monk903556b2017-03-29 13:13:51 -0400240 mTileAdapter.resetTileSpecs(mHost, tiles);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400241 }
242
Jason Monk62b63a02016-02-02 15:15:31 -0500243 private void setTileSpecs() {
244 List<String> specs = new ArrayList<>();
245 for (QSTile tile : mHost.getTiles()) {
246 specs.add(tile.getTileSpec());
247 }
248 mTileAdapter.setTileSpecs(specs);
Jason Monk4efc9242016-03-31 14:10:37 -0400249 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkf7fe83f2015-09-09 13:03:20 -0400250 }
251
Jason Monkbd6dbb02015-09-03 15:46:25 -0400252 private void save() {
Amin Shaikhc5b1bc82018-03-21 08:13:47 -0400253 if (mTileQueryHelper.isFinished()) {
Evan Laird6b284732017-02-28 17:27:04 -0500254 mTileAdapter.saveSpecs(mHost);
255 }
Jason Monk2681dcb2015-09-09 13:50:34 -0400256 }
Jason Monkb9c00192015-10-07 11:45:33 -0400257
Selim Cinek707e2072017-06-30 18:32:40 +0200258
259 public void saveInstanceState(Bundle outState) {
260 if (isShown) {
261 Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
262 }
263 outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing);
264 }
265
266 public void restoreInstanceState(Bundle savedInstanceState) {
267 boolean customizing = savedInstanceState.getBoolean(EXTRA_QS_CUSTOMIZING);
268 if (customizing) {
269 setVisibility(VISIBLE);
270 addOnLayoutChangeListener(new OnLayoutChangeListener() {
271 @Override
272 public void onLayoutChange(View v, int left, int top, int right, int bottom,
273 int oldLeft,
274 int oldTop, int oldRight, int oldBottom) {
275 removeOnLayoutChangeListener(this);
276 showImmediately();
277 }
278 });
279 }
280 }
281
282 public void setEditLocation(int x, int y) {
283 mX = x;
284 mY = y;
285 }
286
Jason Monk9c7844c2017-01-18 15:21:53 -0500287 private final Callback mKeyguardCallback = () -> {
Jason Monk58108412017-03-17 09:47:30 -0400288 if (!isAttachedToWindow()) return;
Jason Monk78a3d602017-03-16 12:12:57 -0400289 if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
Jason Monk9c7844c2017-01-18 15:21:53 -0500290 hide(0, 0);
Jason Monk181d3fe2016-06-06 16:18:50 -0400291 }
292 };
293
Jason Monk8b9d67f2016-03-02 08:59:08 -0500294 private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
295 @Override
296 public void onAnimationEnd(Animator animation) {
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900297 if (isShown) {
298 setCustomizing(true);
299 }
Jason Monk78a3d602017-03-16 12:12:57 -0400300 mOpening = false;
Jason Monk8b9d67f2016-03-02 08:59:08 -0500301 mNotifQsContainer.setCustomizerAnimating(false);
302 }
303
304 @Override
305 public void onAnimationCancel(Animator animation) {
Jason Monk78a3d602017-03-16 12:12:57 -0400306 mOpening = false;
Jason Monk8b9d67f2016-03-02 08:59:08 -0500307 mNotifQsContainer.setCustomizerAnimating(false);
308 }
309 };
310
Jason Monk77a2c5f2016-02-22 15:18:36 -0500311 private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
312 @Override
313 public void onAnimationEnd(Animator animation) {
314 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500315 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500316 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500317 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb213ac42016-06-20 10:27:27 -0400318 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb9c00192015-10-07 11:45:33 -0400319 }
Jason Monkb9c00192015-10-07 11:45:33 -0400320
Jason Monk77a2c5f2016-02-22 15:18:36 -0500321 @Override
322 public void onAnimationCancel(Animator animation) {
323 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500324 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500325 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500326 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb9c00192015-10-07 11:45:33 -0400327 }
Jason Monk77a2c5f2016-02-22 15:18:36 -0500328 };
Jason Monk62b63a02016-02-02 15:15:31 -0500329}