blob: 77da6d4c7b211a1280760c9f66582340f20b8965 [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 Monk96defbe2016-03-29 16:51:03 -040036import com.android.internal.logging.MetricsLogger;
37import com.android.internal.logging.MetricsProto;
Jason Monkbd6dbb02015-09-03 15:46:25 -040038import com.android.systemui.R;
Jason Monk8b9d67f2016-03-02 08:59:08 -050039import com.android.systemui.qs.QSContainer;
Jason Monkb9c00192015-10-07 11:45:33 -040040import com.android.systemui.qs.QSDetailClipper;
Jason Monk62b63a02016-02-02 15:15:31 -050041import com.android.systemui.qs.QSTile;
Jason Monk8b9d67f2016-03-02 08:59:08 -050042import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
Jason Monkbd6dbb02015-09-03 15:46:25 -040043import com.android.systemui.statusbar.phone.PhoneStatusBar;
44import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk181d3fe2016-06-06 16:18:50 -040045import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
Jason Monkbd6dbb02015-09-03 15:46:25 -040046
47import java.util.ArrayList;
Jason Monk62b63a02016-02-02 15:15:31 -050048import java.util.List;
Jason Monkbd6dbb02015-09-03 15:46:25 -040049
50/**
51 * Allows full-screen customization of QS, through show() and hide().
52 *
53 * This adds itself to the status bar window, so it can appear on top of quick settings and
54 * *someday* do fancy animations to get into/out of it.
55 */
Jason Monkb53b6c52016-02-24 17:25:49 -050056public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener {
57
58 private static final int MENU_RESET = Menu.FIRST;
Jason Monkbd6dbb02015-09-03 15:46:25 -040059
Jason Monkb9c00192015-10-07 11:45:33 -040060 private final QSDetailClipper mClipper;
Jason Monkbd6dbb02015-09-03 15:46:25 -040061
62 private PhoneStatusBar mPhoneStatusBar;
63
Jason Monkbd6dbb02015-09-03 15:46:25 -040064 private boolean isShown;
Jason Monkbbadff82015-11-06 15:47:26 -050065 private QSTileHost mHost;
Jason Monk62b63a02016-02-02 15:15:31 -050066 private RecyclerView mRecyclerView;
67 private TileAdapter mTileAdapter;
Jason Monkb53b6c52016-02-24 17:25:49 -050068 private Toolbar mToolbar;
Jason Monk8b9d67f2016-03-02 08:59:08 -050069 private boolean mCustomizing;
70 private NotificationsQuickSettingsContainer mNotifQsContainer;
71 private QSContainer mQsContainer;
Jason Monkbd6dbb02015-09-03 15:46:25 -040072
73 public QSCustomizer(Context context, AttributeSet attrs) {
Jason Monka8321e62016-03-11 08:56:25 -050074 super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
Jason Monkb9c00192015-10-07 11:45:33 -040075 mClipper = new QSDetailClipper(this);
Jason Monkbd6dbb02015-09-03 15:46:25 -040076
Jason Monka8321e62016-03-11 08:56:25 -050077 LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
Jason Monkbd6dbb02015-09-03 15:46:25 -040078
Jason Monkb53b6c52016-02-24 17:25:49 -050079 mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
80 TypedValue value = new TypedValue();
81 mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
82 mToolbar.setNavigationIcon(
Jason Monk04fd2492016-02-29 14:29:26 -050083 getResources().getDrawable(value.resourceId, mContext.getTheme()));
Jason Monkb53b6c52016-02-24 17:25:49 -050084 mToolbar.setNavigationOnClickListener(new OnClickListener() {
85 @Override
86 public void onClick(View v) {
Jason Monkb53b6c52016-02-24 17:25:49 -050087 hide((int) v.getX() + v.getWidth() / 2, (int) v.getY() + v.getHeight() / 2);
88 }
89 });
90 mToolbar.setOnMenuItemClickListener(this);
91 mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
92 mContext.getString(com.android.internal.R.string.reset));
Jason Monkf923db52016-03-02 14:11:14 -050093 mToolbar.setTitle(R.string.qs_edit);
Jason Monkbd6dbb02015-09-03 15:46:25 -040094
Jason Monk62b63a02016-02-02 15:15:31 -050095 mRecyclerView = (RecyclerView) findViewById(android.R.id.list);
96 mTileAdapter = new TileAdapter(getContext());
97 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb53b6c52016-02-24 17:25:49 -050098 mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
Jason Monk62b63a02016-02-02 15:15:31 -050099 GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
100 layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
101 mRecyclerView.setLayoutManager(layout);
102 mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
103 DefaultItemAnimator animator = new DefaultItemAnimator();
104 animator.setMoveDuration(TileAdapter.MOVE_DURATION);
105 mRecyclerView.setItemAnimator(animator);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400106 }
107
Jason Monk931f5462016-05-25 11:19:04 -0400108 @Override
109 protected void onConfigurationChanged(Configuration newConfig) {
110 super.onConfigurationChanged(newConfig);
111 View navBackdrop = findViewById(R.id.nav_bar_background);
112 if (navBackdrop != null) {
113 boolean shouldShow = newConfig.smallestScreenWidthDp >= 600
114 || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
115 navBackdrop.setVisibility(shouldShow ? View.VISIBLE : View.GONE);
116 }
117 }
118
Jason Monka8321e62016-03-11 08:56:25 -0500119 public void setHost(QSTileHost host) {
120 mHost = host;
121 mPhoneStatusBar = host.getPhoneStatusBar();
Jason Monk63554ed2016-04-29 14:58:29 -0400122 mTileAdapter.setHost(host);
Jason Monka8321e62016-03-11 08:56:25 -0500123 }
124
125 public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
126 mNotifQsContainer = notificationsQsContainer;
127 }
128
129 public void setQsContainer(QSContainer qsContainer) {
130 mQsContainer = qsContainer;
131 }
132
Jason Monkb9c00192015-10-07 11:45:33 -0400133 public void show(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500134 if (!isShown) {
Jason Monk96defbe2016-03-29 16:51:03 -0400135 MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500136 isShown = true;
Jason Monk8c7e47f2016-02-17 10:39:31 -0500137 setTileSpecs();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500138 setVisibility(View.VISIBLE);
139 mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500140 new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500141 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400142 mNotifQsContainer.setCustomizerShowing(true);
Jason Monk01213692016-05-04 14:44:02 -0400143 announceForAccessibility(mContext.getString(
144 R.string.accessibility_desc_quick_settings_edit));
Jason Monk181d3fe2016-06-06 16:18:50 -0400145 mHost.getKeyguardMonitor().addCallback(mKeyguardCallback);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500146 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400147 }
148
Jason Monkb9c00192015-10-07 11:45:33 -0400149 public void hide(int x, int y) {
Jason Monk8c7e47f2016-02-17 10:39:31 -0500150 if (isShown) {
Jason Monk96defbe2016-03-29 16:51:03 -0400151 MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500152 isShown = false;
Jason Monkdf8cbc32016-03-16 16:10:56 -0400153 mToolbar.dismissPopupMenus();
Jason Monk8b9d67f2016-03-02 08:59:08 -0500154 setCustomizing(false);
Jason Monk04fd2492016-02-29 14:29:26 -0500155 save();
Jason Monk77a2c5f2016-02-22 15:18:36 -0500156 mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500157 mNotifQsContainer.setCustomizerAnimating(true);
Jason Monk428914d2016-03-30 09:35:36 -0400158 mNotifQsContainer.setCustomizerShowing(false);
Jason Monk01213692016-05-04 14:44:02 -0400159 announceForAccessibility(mContext.getString(
160 R.string.accessibility_desc_quick_settings));
Jason Monk181d3fe2016-06-06 16:18:50 -0400161 mHost.getKeyguardMonitor().removeCallback(mKeyguardCallback);
Jason Monk8c7e47f2016-02-17 10:39:31 -0500162 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400163 }
164
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900165 public boolean isShown() {
166 return isShown;
167 }
168
Jason Monk8b9d67f2016-03-02 08:59:08 -0500169 private void setCustomizing(boolean customizing) {
170 mCustomizing = customizing;
171 mQsContainer.notifyCustomizeChanged();
172 }
173
Jason Monkbd6dbb02015-09-03 15:46:25 -0400174 public boolean isCustomizing() {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500175 return mCustomizing;
Jason Monkbd6dbb02015-09-03 15:46:25 -0400176 }
177
Jason Monkb53b6c52016-02-24 17:25:49 -0500178 @Override
179 public boolean onMenuItemClick(MenuItem item) {
180 switch (item.getItemId()) {
181 case MENU_RESET:
Jason Monk96defbe2016-03-29 16:51:03 -0400182 MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
Jason Monkb53b6c52016-02-24 17:25:49 -0500183 reset();
184 break;
185 }
186 return false;
187 }
188
Jason Monkbd6dbb02015-09-03 15:46:25 -0400189 private void reset() {
190 ArrayList<String> tiles = new ArrayList<>();
Jason Monk9d02a432016-01-20 16:33:46 -0500191 String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
192 for (String tile : defTiles.split(",")) {
Jason Monkbd6dbb02015-09-03 15:46:25 -0400193 tiles.add(tile);
194 }
Jason Monk62b63a02016-02-02 15:15:31 -0500195 mTileAdapter.setTileSpecs(tiles);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400196 }
197
Jason Monk62b63a02016-02-02 15:15:31 -0500198 private void setTileSpecs() {
199 List<String> specs = new ArrayList<>();
200 for (QSTile tile : mHost.getTiles()) {
201 specs.add(tile.getTileSpec());
202 }
203 mTileAdapter.setTileSpecs(specs);
Jason Monk4efc9242016-03-31 14:10:37 -0400204 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkf7fe83f2015-09-09 13:03:20 -0400205 }
206
Jason Monkbd6dbb02015-09-03 15:46:25 -0400207 private void save() {
Jason Monk62b63a02016-02-02 15:15:31 -0500208 mTileAdapter.saveSpecs(mHost);
Jason Monk2681dcb2015-09-09 13:50:34 -0400209 }
Jason Monkb9c00192015-10-07 11:45:33 -0400210
Jason Monk181d3fe2016-06-06 16:18:50 -0400211 private final Callback mKeyguardCallback = new Callback() {
212 @Override
213 public void onKeyguardChanged() {
214 if (mHost.getKeyguardMonitor().isShowing()) {
215 hide(0, 0);
216 }
217 }
218 };
219
Jason Monk8b9d67f2016-03-02 08:59:08 -0500220 private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
221 @Override
222 public void onAnimationEnd(Animator animation) {
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900223 if (isShown) {
224 setCustomizing(true);
225 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500226 mNotifQsContainer.setCustomizerAnimating(false);
227 }
228
229 @Override
230 public void onAnimationCancel(Animator animation) {
231 mNotifQsContainer.setCustomizerAnimating(false);
232 }
233 };
234
Jason Monk77a2c5f2016-02-22 15:18:36 -0500235 private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
236 @Override
237 public void onAnimationEnd(Animator animation) {
238 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500239 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500240 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500241 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb213ac42016-06-20 10:27:27 -0400242 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb9c00192015-10-07 11:45:33 -0400243 }
Jason Monkb9c00192015-10-07 11:45:33 -0400244
Jason Monk77a2c5f2016-02-22 15:18:36 -0500245 @Override
246 public void onAnimationCancel(Animator animation) {
247 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500248 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500249 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500250 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb9c00192015-10-07 11:45:33 -0400251 }
Jason Monk77a2c5f2016-02-22 15:18:36 -0500252 };
Jason Monk62b63a02016-02-02 15:15:31 -0500253}