blob: 9bbead46c33baf6714c9cb80272dc4ba0a828c35 [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;
Jason Monk62b63a02016-02-02 15:15:31 -050023import android.support.v7.widget.DefaultItemAnimator;
24import android.support.v7.widget.GridLayoutManager;
25import android.support.v7.widget.RecyclerView;
Jason Monkbd6dbb02015-09-03 15:46:25 -040026import android.util.AttributeSet;
Jason Monkb53b6c52016-02-24 17:25:49 -050027import android.util.TypedValue;
Jason Monk5db8a412015-10-21 15:16:23 -070028import android.view.ContextThemeWrapper;
Jason Monka8321e62016-03-11 08:56:25 -050029import android.view.LayoutInflater;
Jason Monkb53b6c52016-02-24 17:25:49 -050030import android.view.Menu;
31import android.view.MenuItem;
Jason Monk5db8a412015-10-21 15:16:23 -070032import android.view.View;
Jason Monkbd6dbb02015-09-03 15:46:25 -040033import android.widget.LinearLayout;
Jason Monkb53b6c52016-02-24 17:25:49 -050034import android.widget.Toolbar;
35import android.widget.Toolbar.OnMenuItemClickListener;
Jason Monk46767b72016-08-18 10:58:04 -040036
Jason Monk96defbe2016-03-29 16:51:03 -040037import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010038import com.android.internal.logging.nano.MetricsProto;
Jason Monkbd6dbb02015-09-03 15:46:25 -040039import com.android.systemui.R;
Jason Monk0ceef212016-11-02 14:05:23 -040040import com.android.systemui.plugins.qs.QS;
Jason Monkb9c00192015-10-07 11:45:33 -040041import com.android.systemui.qs.QSDetailClipper;
Jason Monk62b63a02016-02-02 15:15:31 -050042import com.android.systemui.qs.QSTile;
Jason Monk8b9d67f2016-03-02 08:59:08 -050043import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
Jason Monkbd6dbb02015-09-03 15:46:25 -040044import com.android.systemui.statusbar.phone.PhoneStatusBar;
45import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk181d3fe2016-06-06 16:18:50 -040046import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
Jason Monkbd6dbb02015-09-03 15:46:25 -040047
48import java.util.ArrayList;
Jason Monk62b63a02016-02-02 15:15:31 -050049import java.util.List;
Jason Monkbd6dbb02015-09-03 15:46:25 -040050
51/**
52 * Allows full-screen customization of QS, through show() and hide().
53 *
54 * This adds itself to the status bar window, so it can appear on top of quick settings and
55 * *someday* do fancy animations to get into/out of it.
56 */
Jason Monkb53b6c52016-02-24 17:25:49 -050057public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener {
58
59 private static final int MENU_RESET = Menu.FIRST;
Jason Monkbd6dbb02015-09-03 15:46:25 -040060
Jason Monkb9c00192015-10-07 11:45:33 -040061 private final QSDetailClipper mClipper;
Jason Monkbd6dbb02015-09-03 15:46:25 -040062
63 private PhoneStatusBar mPhoneStatusBar;
64
Jason Monkbd6dbb02015-09-03 15:46:25 -040065 private boolean isShown;
Jason Monkbbadff82015-11-06 15:47:26 -050066 private QSTileHost mHost;
Jason Monk62b63a02016-02-02 15:15:31 -050067 private RecyclerView mRecyclerView;
68 private TileAdapter mTileAdapter;
Jason Monkb53b6c52016-02-24 17:25:49 -050069 private Toolbar mToolbar;
Jason Monk8b9d67f2016-03-02 08:59:08 -050070 private boolean mCustomizing;
71 private NotificationsQuickSettingsContainer mNotifQsContainer;
Jason Monk0ceef212016-11-02 14:05:23 -040072 private QS mQs;
Jason Monkbd6dbb02015-09-03 15:46:25 -040073
74 public QSCustomizer(Context context, AttributeSet attrs) {
Jason Monka8321e62016-03-11 08:56:25 -050075 super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
Jason Monkb9c00192015-10-07 11:45:33 -040076 mClipper = new QSDetailClipper(this);
Jason Monkbd6dbb02015-09-03 15:46:25 -040077
Jason Monka8321e62016-03-11 08:56:25 -050078 LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
Jason Monkbd6dbb02015-09-03 15:46:25 -040079
Jason Monkb53b6c52016-02-24 17:25:49 -050080 mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
81 TypedValue value = new TypedValue();
82 mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
83 mToolbar.setNavigationIcon(
Jason Monk04fd2492016-02-29 14:29:26 -050084 getResources().getDrawable(value.resourceId, mContext.getTheme()));
Jason Monkb53b6c52016-02-24 17:25:49 -050085 mToolbar.setNavigationOnClickListener(new OnClickListener() {
86 @Override
87 public void onClick(View v) {
Jason Monkb53b6c52016-02-24 17:25:49 -050088 hide((int) v.getX() + v.getWidth() / 2, (int) v.getY() + v.getHeight() / 2);
89 }
90 });
91 mToolbar.setOnMenuItemClickListener(this);
92 mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
93 mContext.getString(com.android.internal.R.string.reset));
Jason Monkf923db52016-03-02 14:11:14 -050094 mToolbar.setTitle(R.string.qs_edit);
Jason Monkbd6dbb02015-09-03 15:46:25 -040095
Jason Monk62b63a02016-02-02 15:15:31 -050096 mRecyclerView = (RecyclerView) findViewById(android.R.id.list);
97 mTileAdapter = new TileAdapter(getContext());
98 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb53b6c52016-02-24 17:25:49 -050099 mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
Jason Monk62b63a02016-02-02 15:15:31 -0500100 GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
101 layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
102 mRecyclerView.setLayoutManager(layout);
103 mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
104 DefaultItemAnimator animator = new DefaultItemAnimator();
105 animator.setMoveDuration(TileAdapter.MOVE_DURATION);
106 mRecyclerView.setItemAnimator(animator);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400107 }
108
Jason Monk931f5462016-05-25 11:19:04 -0400109 @Override
110 protected void onConfigurationChanged(Configuration newConfig) {
111 super.onConfigurationChanged(newConfig);
112 View navBackdrop = findViewById(R.id.nav_bar_background);
113 if (navBackdrop != null) {
114 boolean shouldShow = newConfig.smallestScreenWidthDp >= 600
115 || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
116 navBackdrop.setVisibility(shouldShow ? View.VISIBLE : View.GONE);
117 }
118 }
119
Jason Monka8321e62016-03-11 08:56:25 -0500120 public void setHost(QSTileHost host) {
121 mHost = host;
122 mPhoneStatusBar = host.getPhoneStatusBar();
Jason Monk63554ed2016-04-29 14:58:29 -0400123 mTileAdapter.setHost(host);
Jason Monka8321e62016-03-11 08:56:25 -0500124 }
125
126 public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
127 mNotifQsContainer = notificationsQsContainer;
128 }
129
Jason Monk0ceef212016-11-02 14:05:23 -0400130 public void setQs(QS qs) {
131 mQs = qs;
Jason Monka8321e62016-03-11 08:56:25 -0500132 }
133
Jason Monkb9c00192015-10-07 11:45:33 -0400134 public void show(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500135 if (!isShown) {
Jason Monk96defbe2016-03-29 16:51:03 -0400136 MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500137 isShown = true;
Jason Monk8c7e47f2016-02-17 10:39:31 -0500138 setTileSpecs();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500139 setVisibility(View.VISIBLE);
140 mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500141 new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500142 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400143 mNotifQsContainer.setCustomizerShowing(true);
Jason Monk01213692016-05-04 14:44:02 -0400144 announceForAccessibility(mContext.getString(
145 R.string.accessibility_desc_quick_settings_edit));
Jason Monk181d3fe2016-06-06 16:18:50 -0400146 mHost.getKeyguardMonitor().addCallback(mKeyguardCallback);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500147 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400148 }
149
Jason Monkb9c00192015-10-07 11:45:33 -0400150 public void hide(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500151 if (isShown) {
Jason Monk96defbe2016-03-29 16:51:03 -0400152 MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500153 isShown = false;
Jason Monkdf8cbc32016-03-16 16:10:56 -0400154 mToolbar.dismissPopupMenus();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500155 setCustomizing(false);
Jason Monk04fd2492016-02-29 14:29:26 -0500156 save();
Jason Monk77a2c5f2016-02-22 15:18:36 -0500157 mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500158 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400159 mNotifQsContainer.setCustomizerShowing(false);
Jason Monk01213692016-05-04 14:44:02 -0400160 announceForAccessibility(mContext.getString(
161 R.string.accessibility_desc_quick_settings));
Jason Monk181d3fe2016-06-06 16:18:50 -0400162 mHost.getKeyguardMonitor().removeCallback(mKeyguardCallback);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500163 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400164 }
165
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900166 public boolean isShown() {
167 return isShown;
168 }
169
Jason Monk8b9d67f2016-03-02 08:59:08 -0500170 private void setCustomizing(boolean customizing) {
171 mCustomizing = customizing;
Jason Monk0ceef212016-11-02 14:05:23 -0400172 mQs.notifyCustomizeChanged();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500173 }
174
Jason Monkbd6dbb02015-09-03 15:46:25 -0400175 public boolean isCustomizing() {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500176 return mCustomizing;
Jason Monkbd6dbb02015-09-03 15:46:25 -0400177 }
178
Jason Monkb53b6c52016-02-24 17:25:49 -0500179 @Override
180 public boolean onMenuItemClick(MenuItem item) {
181 switch (item.getItemId()) {
182 case MENU_RESET:
Jason Monk96defbe2016-03-29 16:51:03 -0400183 MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
Jason Monkb53b6c52016-02-24 17:25:49 -0500184 reset();
185 break;
186 }
187 return false;
188 }
189
Jason Monkbd6dbb02015-09-03 15:46:25 -0400190 private void reset() {
191 ArrayList<String> tiles = new ArrayList<>();
Jason Monk9d02a432016-01-20 16:33:46 -0500192 String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
193 for (String tile : defTiles.split(",")) {
Jason Monkbd6dbb02015-09-03 15:46:25 -0400194 tiles.add(tile);
195 }
Jason Monk62b63a02016-02-02 15:15:31 -0500196 mTileAdapter.setTileSpecs(tiles);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400197 }
198
Jason Monk62b63a02016-02-02 15:15:31 -0500199 private void setTileSpecs() {
200 List<String> specs = new ArrayList<>();
201 for (QSTile tile : mHost.getTiles()) {
202 specs.add(tile.getTileSpec());
203 }
204 mTileAdapter.setTileSpecs(specs);
Jason Monk4efc9242016-03-31 14:10:37 -0400205 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkf7fe83f2015-09-09 13:03:20 -0400206 }
207
Jason Monkbd6dbb02015-09-03 15:46:25 -0400208 private void save() {
Jason Monk62b63a02016-02-02 15:15:31 -0500209 mTileAdapter.saveSpecs(mHost);
Jason Monk2681dcb2015-09-09 13:50:34 -0400210 }
Jason Monkb9c00192015-10-07 11:45:33 -0400211
Jason Monk181d3fe2016-06-06 16:18:50 -0400212 private final Callback mKeyguardCallback = new Callback() {
213 @Override
214 public void onKeyguardChanged() {
215 if (mHost.getKeyguardMonitor().isShowing()) {
216 hide(0, 0);
217 }
218 }
219 };
220
Jason Monk8b9d67f2016-03-02 08:59:08 -0500221 private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
222 @Override
223 public void onAnimationEnd(Animator animation) {
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900224 if (isShown) {
225 setCustomizing(true);
226 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500227 mNotifQsContainer.setCustomizerAnimating(false);
228 }
229
230 @Override
231 public void onAnimationCancel(Animator animation) {
232 mNotifQsContainer.setCustomizerAnimating(false);
233 }
234 };
235
Jason Monk77a2c5f2016-02-22 15:18:36 -0500236 private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
237 @Override
238 public void onAnimationEnd(Animator animation) {
239 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500240 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500241 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500242 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb213ac42016-06-20 10:27:27 -0400243 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb9c00192015-10-07 11:45:33 -0400244 }
Jason Monkb9c00192015-10-07 11:45:33 -0400245
Jason Monk77a2c5f2016-02-22 15:18:36 -0500246 @Override
247 public void onAnimationCancel(Animator animation) {
248 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500249 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500250 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500251 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb9c00192015-10-07 11:45:33 -0400252 }
Jason Monk77a2c5f2016-02-22 15:18:36 -0500253 };
Jason Monk62b63a02016-02-02 15:15:31 -0500254}