blob: b096ead6bbec31b9b52bf61d257e82a1d2cead25 [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 Miller6c9df502013-09-19 15:50:11 -070049import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
Jim Millere898ac52012-04-06 17:10:57 -070050import com.android.systemui.statusbar.phone.PhoneStatusBar;
Jim Millere898ac52012-04-06 17:10:57 -070051
52public class SearchPanelView extends FrameLayout implements
Dianne Hackborn84375872012-06-01 19:03:50 -070053 StatusBarPanel, ActivityOptions.OnAnimationStartedListener {
54 private static final int SEARCH_PANEL_HOLD_DURATION = 0;
Jim Millere898ac52012-04-06 17:10:57 -070055 static final String TAG = "SearchPanelView";
John Spurlockb0e49fc2013-06-12 15:50:50 -040056 static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
Chris Wren64161cc2012-12-17 16:49:30 -050057 public static final boolean DEBUG_GESTURES = true;
Jim Miller3294b6b2012-05-31 17:49:13 -070058 private static final String ASSIST_ICON_METADATA_NAME =
59 "com.android.systemui.action_assist_icon";
Jim Miller9a720f52012-05-30 03:19:43 -070060 private final Context mContext;
Jim Millere898ac52012-04-06 17:10:57 -070061 private BaseStatusBar mBar;
Jim Millere898ac52012-04-06 17:10:57 -070062
63 private boolean mShowing;
64 private View mSearchTargetsContainer;
Jim Miller955a0162012-06-11 21:06:13 -070065 private GlowPadView mGlowPadView;
Jim Miller4eeb4f62012-11-08 00:04:29 -080066 private IWindowManager mWm;
Jim Millere898ac52012-04-06 17:10:57 -070067
68 public SearchPanelView(Context context, AttributeSet attrs) {
69 this(context, attrs, 0);
70 }
71
72 public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
73 super(context, attrs, defStyle);
74 mContext = context;
Jim Miller4eeb4f62012-11-08 00:04:29 -080075 mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
Jim Millere898ac52012-04-06 17:10:57 -070076 }
77
Jim Miller07994402012-05-02 14:22:27 -070078 private void startAssistActivity() {
Daniel Sandlerd5483c32012-10-19 16:44:15 -040079 if (!mBar.isDeviceProvisioned()) return;
80
Fabrice Di Meglio4d0d38b2012-05-25 11:36:03 -070081 // Close Recent Apps if needed
Daniel Sandler11cf1782012-09-27 14:03:08 -040082 mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
Jim Millerbac6d4b2012-11-08 01:20:55 -080083 boolean isKeyguardShowing = false;
Daniel Sandlerd5483c32012-10-19 16:44:15 -040084 try {
Jim Millerbac6d4b2012-11-08 01:20:55 -080085 isKeyguardShowing = mWm.isKeyguardLocked();
Daniel Sandlerd5483c32012-10-19 16:44:15 -040086 } catch (RemoteException e) {
Jim Millerbac6d4b2012-11-08 01:20:55 -080087
Daniel Sandlerd5483c32012-10-19 16:44:15 -040088 }
Jim Millerbac6d4b2012-11-08 01:20:55 -080089
90 if (isKeyguardShowing) {
91 // Have keyguard show the bouncer and launch the activity if the user succeeds.
Jim Miller6c9df502013-09-19 15:50:11 -070092 KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
Jim Millerbac6d4b2012-11-08 01:20:55 -080093 onAnimationStarted();
94 } else {
95 // Otherwise, keyguard isn't showing so launch it from here.
96 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -080097 .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
Jim Millerbac6d4b2012-11-08 01:20:55 -080098 if (intent == null) return;
99
100 try {
101 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
102 } catch (RemoteException e) {
103 // too bad, so sad...
104 }
105
106 try {
107 ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
108 R.anim.search_launch_enter, R.anim.search_launch_exit,
109 getHandler(), this);
110 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
111 mContext.startActivityAsUser(intent, opts.toBundle(),
112 new UserHandle(UserHandle.USER_CURRENT));
113 } catch (ActivityNotFoundException e) {
John Spurlockcd686b52013-06-05 10:13:46 -0400114 Log.w(TAG, "Activity not found for " + intent.getAction());
Jim Millerbac6d4b2012-11-08 01:20:55 -0800115 onAnimationStarted();
116 }
117 }
Jim Miller07994402012-05-02 14:22:27 -0700118 }
119
Jim Miller955a0162012-06-11 21:06:13 -0700120 class GlowPadTriggerListener implements GlowPadView.OnTriggerListener {
Dianne Hackborn84375872012-06-01 19:03:50 -0700121 boolean mWaitingForLaunch;
Jim Miller998bb762012-05-21 13:37:27 -0700122
Jim Millere898ac52012-04-06 17:10:57 -0700123 public void onGrabbed(View v, int handle) {
124 }
125
126 public void onReleased(View v, int handle) {
127 }
128
129 public void onGrabbedStateChange(View v, int handle) {
Jim Millera073e572012-05-23 17:03:27 -0700130 if (!mWaitingForLaunch && OnTriggerListener.NO_HANDLE == handle) {
Jim Millere898ac52012-04-06 17:10:57 -0700131 mBar.hideSearchPanel();
132 }
133 }
134
Jim Millera073e572012-05-23 17:03:27 -0700135 public void onTrigger(View v, final int target) {
Jim Miller955a0162012-06-11 21:06:13 -0700136 final int resId = mGlowPadView.getResourceIdForTarget(target);
Jim Millera073e572012-05-23 17:03:27 -0700137 switch (resId) {
John Spurlock74d24802013-12-02 17:14:51 -0500138 case R.drawable.ic_action_assist_generic:
Jim Millera073e572012-05-23 17:03:27 -0700139 mWaitingForLaunch = true;
140 startAssistActivity();
Philip Milnea01a2a892012-05-29 14:46:54 -0700141 vibrate();
Philip Milnea01a2a892012-05-29 14:46:54 -0700142 break;
Jim Millera073e572012-05-23 17:03:27 -0700143 }
Jim Miller998bb762012-05-21 13:37:27 -0700144 }
145
146 public void onFinishFinalAnimation() {
Jim Miller3b291872012-04-24 18:54:20 -0700147 }
Dianne Hackborn84375872012-06-01 19:03:50 -0700148 }
Jim Miller955a0162012-06-11 21:06:13 -0700149 final GlowPadTriggerListener mGlowPadViewListener = new GlowPadTriggerListener();
Dianne Hackborn84375872012-06-01 19:03:50 -0700150
151 @Override
152 public void onAnimationStarted() {
153 postDelayed(new Runnable() {
154 public void run() {
Jim Miller955a0162012-06-11 21:06:13 -0700155 mGlowPadViewListener.mWaitingForLaunch = false;
Dianne Hackborn84375872012-06-01 19:03:50 -0700156 mBar.hideSearchPanel();
157 }
158 }, SEARCH_PANEL_HOLD_DURATION);
159 }
Jim Millere898ac52012-04-06 17:10:57 -0700160
161 @Override
162 protected void onFinishInflate() {
163 super.onFinishInflate();
164 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Jim Miller9a720f52012-05-30 03:19:43 -0700165 mSearchTargetsContainer = findViewById(R.id.search_panel_container);
Jim Millere898ac52012-04-06 17:10:57 -0700166 // TODO: fetch views
Jim Miller955a0162012-06-11 21:06:13 -0700167 mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
168 mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
Jim Miller45308b12012-06-18 19:23:39 -0700169 }
170
171 private void maybeSwapSearchIcon() {
Amith Yamasanic1d07a42012-08-14 09:32:02 -0700172 Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -0800173 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
Jim Miller45308b12012-06-18 19:23:39 -0700174 if (intent != null) {
175 ComponentName component = intent.getComponent();
176 if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
177 ASSIST_ICON_METADATA_NAME,
John Spurlock74d24802013-12-02 17:14:51 -0500178 R.drawable.ic_action_assist_generic)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400179 if (DEBUG) Log.v(TAG, "Couldn't grab icon for component " + component);
Jim Miller3294b6b2012-05-31 17:49:13 -0700180 }
Jim Miller3294b6b2012-05-31 17:49:13 -0700181 }
Jim Millere898ac52012-04-06 17:10:57 -0700182 }
183
184 private boolean pointInside(int x, int y, View v) {
185 final int l = v.getLeft();
186 final int r = v.getRight();
187 final int t = v.getTop();
188 final int b = v.getBottom();
189 return x >= l && x < r && y >= t && y < b;
190 }
191
192 public boolean isInContentArea(int x, int y) {
John Spurlockb0e49fc2013-06-12 15:50:50 -0400193 return pointInside(x, y, mSearchTargetsContainer);
Jim Millere898ac52012-04-06 17:10:57 -0700194 }
195
Jim Miller9a720f52012-05-30 03:19:43 -0700196 private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
Jim Miller20830422012-05-15 20:46:03 -0700197 public boolean onPreDraw() {
198 getViewTreeObserver().removeOnPreDrawListener(this);
Jim Miller955a0162012-06-11 21:06:13 -0700199 mGlowPadView.resumeAnimations();
Jim Miller20830422012-05-15 20:46:03 -0700200 return false;
201 }
202 };
203
Philip Milnea01a2a892012-05-29 14:46:54 -0700204 private void vibrate() {
205 Context context = getContext();
Jeff Sharkey723a7252012-10-12 14:26:31 -0700206 if (Settings.System.getIntForUser(context.getContentResolver(),
207 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
Philip Milnea01a2a892012-05-29 14:46:54 -0700208 Resources res = context.getResources();
209 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
210 vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
211 }
212 }
213
Jim Millerd99e7fd2012-05-08 16:30:42 -0700214 public void show(final boolean show, boolean animate) {
Jim Millera073e572012-05-23 17:03:27 -0700215 if (!show) {
216 final LayoutTransition transitioner = animate ? createLayoutTransitioner() : null;
Philip Milnea01a2a892012-05-29 14:46:54 -0700217 ((ViewGroup) mSearchTargetsContainer).setLayoutTransition(transitioner);
Jim Millere898ac52012-04-06 17:10:57 -0700218 }
Jim Millera073e572012-05-23 17:03:27 -0700219 mShowing = show;
Jim Miller20830422012-05-15 20:46:03 -0700220 if (show) {
Jim Miller45308b12012-06-18 19:23:39 -0700221 maybeSwapSearchIcon();
Jim Miller20830422012-05-15 20:46:03 -0700222 if (getVisibility() != View.VISIBLE) {
223 setVisibility(View.VISIBLE);
224 // Don't start the animation until we've created the layer, which is done
225 // right before we are drawn
Jim Miller955a0162012-06-11 21:06:13 -0700226 mGlowPadView.suspendAnimations();
227 mGlowPadView.ping();
Jim Miller20830422012-05-15 20:46:03 -0700228 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
Philip Milnea01a2a892012-05-29 14:46:54 -0700229 vibrate();
Jim Millerd99e7fd2012-05-08 16:30:42 -0700230 }
Jim Miller20830422012-05-15 20:46:03 -0700231 setFocusable(true);
232 setFocusableInTouchMode(true);
233 requestFocus();
234 } else {
235 setVisibility(View.INVISIBLE);
236 }
Jim Millere898ac52012-04-06 17:10:57 -0700237 }
238
239 public void hide(boolean animate) {
Jim Millere898ac52012-04-06 17:10:57 -0700240 if (mBar != null) {
241 // This will indirectly cause show(false, ...) to get called
Daniel Sandler11cf1782012-09-27 14:03:08 -0400242 mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Jim Millerd99e7fd2012-05-08 16:30:42 -0700243 } else {
244 setVisibility(View.INVISIBLE);
Jim Millere898ac52012-04-06 17:10:57 -0700245 }
246 }
247
Jim Millere898ac52012-04-06 17:10:57 -0700248 /**
249 * We need to be aligned at the bottom. LinearLayout can't do this, so instead,
250 * let LinearLayout do all the hard work, and then shift everything down to the bottom.
251 */
252 @Override
253 protected void onLayout(boolean changed, int l, int t, int r, int b) {
254 super.onLayout(changed, l, t, r, b);
255 // setPanelHeight(mSearchTargetsContainer.getHeight());
256 }
257
258 @Override
259 public boolean dispatchHoverEvent(MotionEvent event) {
260 // Ignore hover events outside of this panel bounds since such events
261 // generate spurious accessibility events with the panel content when
262 // tapping outside of it, thus confusing the user.
263 final int x = (int) event.getX();
264 final int y = (int) event.getY();
265 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
266 return super.dispatchHoverEvent(event);
267 }
268 return true;
269 }
270
271 /**
272 * Whether the panel is showing, or, if it's animating, whether it will be
273 * when the animation is done.
274 */
275 public boolean isShowing() {
276 return mShowing;
277 }
278
279 public void setBar(BaseStatusBar bar) {
280 mBar = bar;
281 }
282
Chris Wren64161cc2012-12-17 16:49:30 -0500283 @Override
284 public boolean onTouchEvent(MotionEvent event) {
285 if (DEBUG_GESTURES) {
286 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
287 EventLog.writeEvent(EventLogTags.SYSUI_SEARCHPANEL_TOUCH,
288 event.getActionMasked(), (int) event.getX(), (int) event.getY());
289 }
290 }
291 return super.onTouchEvent(event);
292 }
293
Jim Millera073e572012-05-23 17:03:27 -0700294 private LayoutTransition createLayoutTransitioner() {
295 LayoutTransition transitioner = new LayoutTransition();
Jim Millere898ac52012-04-06 17:10:57 -0700296 transitioner.setDuration(200);
297 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
298 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
Jim Millera073e572012-05-23 17:03:27 -0700299 return transitioner;
Jim Millere898ac52012-04-06 17:10:57 -0700300 }
Jim Miller45308b12012-06-18 19:23:39 -0700301
302 public boolean isAssistantAvailable() {
Amith Yamasanic1d07a42012-08-14 09:32:02 -0700303 return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
Dianne Hackbornf9c5e0f2013-01-23 14:39:13 -0800304 .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
Jim Miller45308b12012-06-18 19:23:39 -0700305 }
Jim Millere898ac52012-04-06 17:10:57 -0700306}