blob: 0de1e3022b6c92805895368fb04d6994452a7933 [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
Jason Monk8b9d67f2016-03-02 08:59:08 -0500165 private void setCustomizing(boolean customizing) {
166 mCustomizing = customizing;
167 mQsContainer.notifyCustomizeChanged();
168 }
169
Jason Monkbd6dbb02015-09-03 15:46:25 -0400170 public boolean isCustomizing() {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500171 return mCustomizing;
Jason Monkbd6dbb02015-09-03 15:46:25 -0400172 }
173
Jason Monkb53b6c52016-02-24 17:25:49 -0500174 @Override
175 public boolean onMenuItemClick(MenuItem item) {
176 switch (item.getItemId()) {
177 case MENU_RESET:
Jason Monk96defbe2016-03-29 16:51:03 -0400178 MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
Jason Monkb53b6c52016-02-24 17:25:49 -0500179 reset();
180 break;
181 }
182 return false;
183 }
184
Jason Monkbd6dbb02015-09-03 15:46:25 -0400185 private void reset() {
186 ArrayList<String> tiles = new ArrayList<>();
Jason Monk9d02a432016-01-20 16:33:46 -0500187 String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
188 for (String tile : defTiles.split(",")) {
Jason Monkbd6dbb02015-09-03 15:46:25 -0400189 tiles.add(tile);
190 }
Jason Monk62b63a02016-02-02 15:15:31 -0500191 mTileAdapter.setTileSpecs(tiles);
Jason Monkbd6dbb02015-09-03 15:46:25 -0400192 }
193
Jason Monk62b63a02016-02-02 15:15:31 -0500194 private void setTileSpecs() {
195 List<String> specs = new ArrayList<>();
196 for (QSTile tile : mHost.getTiles()) {
197 specs.add(tile.getTileSpec());
198 }
199 mTileAdapter.setTileSpecs(specs);
Jason Monk4efc9242016-03-31 14:10:37 -0400200 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkf7fe83f2015-09-09 13:03:20 -0400201 }
202
Jason Monkbd6dbb02015-09-03 15:46:25 -0400203 private void save() {
Jason Monk62b63a02016-02-02 15:15:31 -0500204 mTileAdapter.saveSpecs(mHost);
Jason Monk2681dcb2015-09-09 13:50:34 -0400205 }
Jason Monkb9c00192015-10-07 11:45:33 -0400206
Jason Monk181d3fe2016-06-06 16:18:50 -0400207 private final Callback mKeyguardCallback = new Callback() {
208 @Override
209 public void onKeyguardChanged() {
210 if (mHost.getKeyguardMonitor().isShowing()) {
211 hide(0, 0);
212 }
213 }
214 };
215
Jason Monk8b9d67f2016-03-02 08:59:08 -0500216 private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
217 @Override
218 public void onAnimationEnd(Animator animation) {
219 setCustomizing(true);
220 mNotifQsContainer.setCustomizerAnimating(false);
221 }
222
223 @Override
224 public void onAnimationCancel(Animator animation) {
225 mNotifQsContainer.setCustomizerAnimating(false);
226 }
227 };
228
Jason Monk77a2c5f2016-02-22 15:18:36 -0500229 private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
230 @Override
231 public void onAnimationEnd(Animator animation) {
232 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500233 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500234 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500235 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb213ac42016-06-20 10:27:27 -0400236 mRecyclerView.setAdapter(mTileAdapter);
Jason Monkb9c00192015-10-07 11:45:33 -0400237 }
Jason Monkb9c00192015-10-07 11:45:33 -0400238
Jason Monk77a2c5f2016-02-22 15:18:36 -0500239 @Override
240 public void onAnimationCancel(Animator animation) {
241 if (!isShown) {
Jason Monk8b9d67f2016-03-02 08:59:08 -0500242 setVisibility(View.GONE);
Jason Monk77a2c5f2016-02-22 15:18:36 -0500243 }
Jason Monk8b9d67f2016-03-02 08:59:08 -0500244 mNotifQsContainer.setCustomizerAnimating(false);
Jason Monkb9c00192015-10-07 11:45:33 -0400245 }
Jason Monk77a2c5f2016-02-22 15:18:36 -0500246 };
Jason Monk62b63a02016-02-02 15:15:31 -0500247}