blob: c32f741b2ffac493d144bcdfb0c736bc272c196b [file] [log] [blame]
Jim Millere898ac52012-04-06 17:10:57 -07001/*
2 * Copyright (C) 2012 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;
18
Jim Millere898ac52012-04-06 17:10:57 -070019import android.animation.LayoutTransition;
Daniel Sandlerd5483c32012-10-19 16:44:15 -040020import android.app.ActivityManagerNative;
Jim Miller998bb762012-05-21 13:37:27 -070021import android.app.ActivityOptions;
Jim Miller3b291872012-04-24 18:54:20 -070022import android.app.SearchManager;
Jim Millere898ac52012-04-06 17:10:57 -070023import android.content.ActivityNotFoundException;
Jim Miller3b291872012-04-24 18:54:20 -070024import android.content.ComponentName;
Jim Millere898ac52012-04-06 17:10:57 -070025import android.content.Context;
26import android.content.Intent;
Philip Milnea01a2a892012-05-29 14:46:54 -070027import android.content.res.Resources;
Daniel Sandlerd5483c32012-10-19 16:44:15 -040028import android.os.RemoteException;
Jim Miller4eeb4f62012-11-08 00:04:29 -080029import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070030import android.os.UserHandle;
Philip Milnea01a2a892012-05-29 14:46:54 -070031import android.os.Vibrator;
32import android.provider.Settings;
Jim Millere898ac52012-04-06 17:10:57 -070033import android.util.AttributeSet;
Chris Wren64161cc2012-12-17 16:49:30 -050034import android.util.EventLog;
John Spurlockcd686b52013-06-05 10:13:46 -040035import android.util.Log;
Jim Miller4eeb4f62012-11-08 00:04:29 -080036import android.view.IWindowManager;
Jim Millere898ac52012-04-06 17:10:57 -070037import android.view.MotionEvent;
38import android.view.View;
39import android.view.ViewGroup;
Jim Miller20830422012-05-15 20:46:03 -070040import android.view.ViewTreeObserver;
41import android.view.ViewTreeObserver.OnPreDrawListener;
Jim Millere898ac52012-04-06 17:10:57 -070042import android.widget.FrameLayout;
Jeff Brownde7a8ea2012-06-13 18:28:57 -070043
Jim Miller955a0162012-06-11 21:06:13 -070044import com.android.internal.widget.multiwaveview.GlowPadView;
45import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
Jim Millere898ac52012-04-06 17:10:57 -070046import com.android.systemui.statusbar.BaseStatusBar;
Jim Miller9a720f52012-05-30 03:19:43 -070047import com.android.systemui.statusbar.CommandQueue;
John Spurlockb0e49fc2013-06-12 15:50:50 -040048import com.android.systemui.statusbar.StatusBarPanel;
Jim Millere898ac52012-04-06 17:10:57 -070049import com.android.systemui.statusbar.phone.PhoneStatusBar;
Jim Millere898ac52012-04-06 17:10:57 -070050
51public class SearchPanelView extends FrameLayout implements
Dianne Hackborn84375872012-06-01 19:03:50 -070052 StatusBarPanel, ActivityOptions.OnAnimationStartedListener {
53 private static final int SEARCH_PANEL_HOLD_DURATION = 0;
Jim Millere898ac52012-04-06 17:10:57 -070054 static final String TAG = "SearchPanelView";
John Spurlockb0e49fc2013-06-12 15:50:50 -040055 static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
Chris Wren64161cc2012-12-17 16:49:30 -050056 public static final boolean DEBUG_GESTURES = true;
Jim Miller3294b6b2012-05-31 17:49:13 -070057 private static final String ASSIST_ICON_METADATA_NAME =
58 "com.android.systemui.action_assist_icon";
Jim Miller9a720f52012-05-30 03:19:43 -070059 private final Context mContext;
Jim Millere898ac52012-04-06 17:10:57 -070060 private BaseStatusBar mBar;
Jim Millere898ac52012-04-06 17:10:57 -070061
62 private boolean mShowing;
63 private View mSearchTargetsContainer;
Jim Miller955a0162012-06-11 21:06:13 -070064 private GlowPadView mGlowPadView;
Jim Miller4eeb4f62012-11-08 00:04:29 -080065 private IWindowManager mWm;
Jim Millere898ac52012-04-06 17:10:57 -070066
67 public SearchPanelView(Context context, AttributeSet attrs) {
68 this(context, attrs, 0);
69 }
70
71 public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
72 super(context, attrs, defStyle);
73 mContext = context;
Jim Miller4eeb4f62012-11-08 00:04:29 -080074 mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
Jim Millere898ac52012-04-06 17:10:57 -070075 }
76
Jim Miller07994402012-05-02 14:22:27 -070077 private void startAssistActivity() {
Daniel Sandlerd5483c32012-10-19 16:44:15 -040078 if (!mBar.isDeviceProvisioned()) return;
79
Fabrice Di Meglio4d0d38b2012-05-25 11:36:03 -070080 // Close Recent Apps if needed
Daniel Sandler11cf1782012-09-27 14:03:08 -040081 mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
Jim Millerbac6d4b2012-11-08 01:20:55 -080082 boolean isKeyguardShowing = false;
Daniel Sandlerd5483c32012-10-19 16:44:15 -040083 try {
Jim Millerbac6d4b2012-11-08 01:20:55 -080084 isKeyguardShowing = mWm.isKeyguardLocked();
Daniel Sandlerd5483c32012-10-19 16:44:15 -040085 } catch (RemoteException e) {
Jim Millerbac6d4b2012-11-08 01:20:55 -080086
Daniel Sandlerd5483c32012-10-19 16:44:15 -040087 }
Jim Millerbac6d4b2012-11-08 01:20:55 -080088
89 if (isKeyguardShowing) {
90 // Have keyguard show the bouncer and launch the activity if the user succeeds.
91 try {
92 mWm.showAssistant();
93 } catch (RemoteException e) {
94 // too bad, so sad...
95 }
96 onAnimationStarted();
97 } else {
98 // Otherwise, keyguard isn't showing so launch it from here.
99 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -0800100 .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
Jim Millerbac6d4b2012-11-08 01:20:55 -0800101 if (intent == null) return;
102
103 try {
104 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
105 } catch (RemoteException e) {
106 // too bad, so sad...
107 }
108
109 try {
110 ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
111 R.anim.search_launch_enter, R.anim.search_launch_exit,
112 getHandler(), this);
113 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
114 mContext.startActivityAsUser(intent, opts.toBundle(),
115 new UserHandle(UserHandle.USER_CURRENT));
116 } catch (ActivityNotFoundException e) {
John Spurlockcd686b52013-06-05 10:13:46 -0400117 Log.w(TAG, "Activity not found for " + intent.getAction());
Jim Millerbac6d4b2012-11-08 01:20:55 -0800118 onAnimationStarted();
119 }
120 }
Jim Miller07994402012-05-02 14:22:27 -0700121 }
122
Jim Miller955a0162012-06-11 21:06:13 -0700123 class GlowPadTriggerListener implements GlowPadView.OnTriggerListener {
Dianne Hackborn84375872012-06-01 19:03:50 -0700124 boolean mWaitingForLaunch;
Jim Miller998bb762012-05-21 13:37:27 -0700125
Jim Millere898ac52012-04-06 17:10:57 -0700126 public void onGrabbed(View v, int handle) {
127 }
128
129 public void onReleased(View v, int handle) {
130 }
131
132 public void onGrabbedStateChange(View v, int handle) {
Jim Millera073e572012-05-23 17:03:27 -0700133 if (!mWaitingForLaunch && OnTriggerListener.NO_HANDLE == handle) {
Jim Millere898ac52012-04-06 17:10:57 -0700134 mBar.hideSearchPanel();
135 }
136 }
137
Jim Millera073e572012-05-23 17:03:27 -0700138 public void onTrigger(View v, final int target) {
Jim Miller955a0162012-06-11 21:06:13 -0700139 final int resId = mGlowPadView.getResourceIdForTarget(target);
Jim Millera073e572012-05-23 17:03:27 -0700140 switch (resId) {
Jim Miller20039ad2012-06-15 17:26:28 -0700141 case com.android.internal.R.drawable.ic_action_assist_generic:
Jim Millera073e572012-05-23 17:03:27 -0700142 mWaitingForLaunch = true;
143 startAssistActivity();
Philip Milnea01a2a892012-05-29 14:46:54 -0700144 vibrate();
Philip Milnea01a2a892012-05-29 14:46:54 -0700145 break;
Jim Millera073e572012-05-23 17:03:27 -0700146 }
Jim Miller998bb762012-05-21 13:37:27 -0700147 }
148
149 public void onFinishFinalAnimation() {
Jim Miller3b291872012-04-24 18:54:20 -0700150 }
Dianne Hackborn84375872012-06-01 19:03:50 -0700151 }
Jim Miller955a0162012-06-11 21:06:13 -0700152 final GlowPadTriggerListener mGlowPadViewListener = new GlowPadTriggerListener();
Dianne Hackborn84375872012-06-01 19:03:50 -0700153
154 @Override
155 public void onAnimationStarted() {
156 postDelayed(new Runnable() {
157 public void run() {
Jim Miller955a0162012-06-11 21:06:13 -0700158 mGlowPadViewListener.mWaitingForLaunch = false;
Dianne Hackborn84375872012-06-01 19:03:50 -0700159 mBar.hideSearchPanel();
160 }
161 }, SEARCH_PANEL_HOLD_DURATION);
162 }
Jim Millere898ac52012-04-06 17:10:57 -0700163
164 @Override
165 protected void onFinishInflate() {
166 super.onFinishInflate();
167 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Jim Miller9a720f52012-05-30 03:19:43 -0700168 mSearchTargetsContainer = findViewById(R.id.search_panel_container);
Jim Millere898ac52012-04-06 17:10:57 -0700169 // TODO: fetch views
Jim Miller955a0162012-06-11 21:06:13 -0700170 mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
171 mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
Jim Miller45308b12012-06-18 19:23:39 -0700172 }
173
174 private void maybeSwapSearchIcon() {
Amith Yamasanic1d07a42012-08-14 09:32:02 -0700175 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -0800176 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
Jim Miller45308b12012-06-18 19:23:39 -0700177 if (intent != null) {
178 ComponentName component = intent.getComponent();
179 if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
180 ASSIST_ICON_METADATA_NAME,
181 com.android.internal.R.drawable.ic_action_assist_generic)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400182 if (DEBUG) Log.v(TAG, "Couldn't grab icon for component " + component);
Jim Miller3294b6b2012-05-31 17:49:13 -0700183 }
Jim Miller3294b6b2012-05-31 17:49:13 -0700184 }
Jim Millere898ac52012-04-06 17:10:57 -0700185 }
186
187 private boolean pointInside(int x, int y, View v) {
188 final int l = v.getLeft();
189 final int r = v.getRight();
190 final int t = v.getTop();
191 final int b = v.getBottom();
192 return x >= l && x < r && y >= t && y < b;
193 }
194
195 public boolean isInContentArea(int x, int y) {
John Spurlockb0e49fc2013-06-12 15:50:50 -0400196 return pointInside(x, y, mSearchTargetsContainer);
Jim Millere898ac52012-04-06 17:10:57 -0700197 }
198
Jim Miller9a720f52012-05-30 03:19:43 -0700199 private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
Jim Miller20830422012-05-15 20:46:03 -0700200 public boolean onPreDraw() {
201 getViewTreeObserver().removeOnPreDrawListener(this);
Jim Miller955a0162012-06-11 21:06:13 -0700202 mGlowPadView.resumeAnimations();
Jim Miller20830422012-05-15 20:46:03 -0700203 return false;
204 }
205 };
206
Philip Milnea01a2a892012-05-29 14:46:54 -0700207 private void vibrate() {
208 Context context = getContext();
Jeff Sharkey723a7252012-10-12 14:26:31 -0700209 if (Settings.System.getIntForUser(context.getContentResolver(),
210 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
Philip Milnea01a2a892012-05-29 14:46:54 -0700211 Resources res = context.getResources();
212 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
213 vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
214 }
215 }
216
Jim Millerd99e7fd2012-05-08 16:30:42 -0700217 public void show(final boolean show, boolean animate) {
Jim Millera073e572012-05-23 17:03:27 -0700218 if (!show) {
219 final LayoutTransition transitioner = animate ? createLayoutTransitioner() : null;
Philip Milnea01a2a892012-05-29 14:46:54 -0700220 ((ViewGroup) mSearchTargetsContainer).setLayoutTransition(transitioner);
Jim Millere898ac52012-04-06 17:10:57 -0700221 }
Jim Millera073e572012-05-23 17:03:27 -0700222 mShowing = show;
Jim Miller20830422012-05-15 20:46:03 -0700223 if (show) {
Jim Miller45308b12012-06-18 19:23:39 -0700224 maybeSwapSearchIcon();
Jim Miller20830422012-05-15 20:46:03 -0700225 if (getVisibility() != View.VISIBLE) {
226 setVisibility(View.VISIBLE);
227 // Don't start the animation until we've created the layer, which is done
228 // right before we are drawn
Jim Miller955a0162012-06-11 21:06:13 -0700229 mGlowPadView.suspendAnimations();
230 mGlowPadView.ping();
Jim Miller20830422012-05-15 20:46:03 -0700231 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
Philip Milnea01a2a892012-05-29 14:46:54 -0700232 vibrate();
Jim Millerd99e7fd2012-05-08 16:30:42 -0700233 }
Jim Miller20830422012-05-15 20:46:03 -0700234 setFocusable(true);
235 setFocusableInTouchMode(true);
236 requestFocus();
237 } else {
238 setVisibility(View.INVISIBLE);
239 }
Jim Millere898ac52012-04-06 17:10:57 -0700240 }
241
242 public void hide(boolean animate) {
Jim Millere898ac52012-04-06 17:10:57 -0700243 if (mBar != null) {
244 // This will indirectly cause show(false, ...) to get called
Daniel Sandler11cf1782012-09-27 14:03:08 -0400245 mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Jim Millerd99e7fd2012-05-08 16:30:42 -0700246 } else {
247 setVisibility(View.INVISIBLE);
Jim Millere898ac52012-04-06 17:10:57 -0700248 }
249 }
250
Jim Millere898ac52012-04-06 17:10:57 -0700251 /**
252 * We need to be aligned at the bottom. LinearLayout can't do this, so instead,
253 * let LinearLayout do all the hard work, and then shift everything down to the bottom.
254 */
255 @Override
256 protected void onLayout(boolean changed, int l, int t, int r, int b) {
257 super.onLayout(changed, l, t, r, b);
258 // setPanelHeight(mSearchTargetsContainer.getHeight());
259 }
260
261 @Override
262 public boolean dispatchHoverEvent(MotionEvent event) {
263 // Ignore hover events outside of this panel bounds since such events
264 // generate spurious accessibility events with the panel content when
265 // tapping outside of it, thus confusing the user.
266 final int x = (int) event.getX();
267 final int y = (int) event.getY();
268 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
269 return super.dispatchHoverEvent(event);
270 }
271 return true;
272 }
273
274 /**
275 * Whether the panel is showing, or, if it's animating, whether it will be
276 * when the animation is done.
277 */
278 public boolean isShowing() {
279 return mShowing;
280 }
281
282 public void setBar(BaseStatusBar bar) {
283 mBar = bar;
284 }
285
Chris Wren64161cc2012-12-17 16:49:30 -0500286 @Override
287 public boolean onTouchEvent(MotionEvent event) {
288 if (DEBUG_GESTURES) {
289 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
290 EventLog.writeEvent(EventLogTags.SYSUI_SEARCHPANEL_TOUCH,
291 event.getActionMasked(), (int) event.getX(), (int) event.getY());
292 }
293 }
294 return super.onTouchEvent(event);
295 }
296
Jim Millera073e572012-05-23 17:03:27 -0700297 private LayoutTransition createLayoutTransitioner() {
298 LayoutTransition transitioner = new LayoutTransition();
Jim Millere898ac52012-04-06 17:10:57 -0700299 transitioner.setDuration(200);
300 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
301 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
Jim Millera073e572012-05-23 17:03:27 -0700302 return transitioner;
Jim Millere898ac52012-04-06 17:10:57 -0700303 }
Jim Miller45308b12012-06-18 19:23:39 -0700304
305 public boolean isAssistantAvailable() {
Amith Yamasanic1d07a42012-08-14 09:32:02 -0700306 return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -0800307 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
Jim Miller45308b12012-06-18 19:23:39 -0700308 }
Jim Millere898ac52012-04-06 17:10:57 -0700309}