SearchPanelView.java revision 4d0d38b9f41d310a797e01aaa1db2e819379e5bd
1/* 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 19import android.animation.Animator; 20import android.animation.LayoutTransition; 21import android.app.ActivityManagerNative; 22import android.app.ActivityOptions; 23import android.app.SearchManager; 24import android.content.ActivityNotFoundException; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.pm.PackageManager; 29import android.util.AttributeSet; 30import android.util.Log; 31import android.util.Slog; 32import android.view.MotionEvent; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.ViewTreeObserver; 36import android.view.ViewTreeObserver.OnPreDrawListener; 37import android.widget.FrameLayout; 38 39import com.android.internal.widget.multiwaveview.MultiWaveView; 40import com.android.internal.widget.multiwaveview.MultiWaveView.OnTriggerListener; 41import com.android.server.am.ActivityManagerService; 42import com.android.systemui.R; 43import com.android.systemui.recent.StatusBarTouchProxy; 44import com.android.systemui.statusbar.BaseStatusBar; 45import com.android.systemui.statusbar.phone.PhoneStatusBar; 46import com.android.systemui.statusbar.tablet.StatusBarPanel; 47import com.android.systemui.statusbar.tablet.TabletStatusBar; 48 49public class SearchPanelView extends FrameLayout implements 50 StatusBarPanel, Animator.AnimatorListener { 51 static final String TAG = "SearchPanelView"; 52 static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; 53 private Context mContext; 54 private BaseStatusBar mBar; 55 private StatusBarTouchProxy mStatusBarTouchProxy; 56 57 private boolean mShowing; 58 private View mSearchTargetsContainer; 59 private MultiWaveView mMultiWaveView; 60 61 public SearchPanelView(Context context, AttributeSet attrs) { 62 this(context, attrs, 0); 63 } 64 65 public SearchPanelView(Context context, AttributeSet attrs, int defStyle) { 66 super(context, attrs, defStyle); 67 mContext = context; 68 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 69 if (mSearchManager == null) { 70 Slog.w(TAG, "Search manager not available"); 71 } 72 } 73 74 private SearchManager mSearchManager; 75 76 // This code should be the same as that used in LockScreen.java 77 public boolean isAssistantAvailable() { 78 Intent intent = getAssistIntent(); 79 return intent == null ? false 80 : mContext.getPackageManager().queryIntentActivities(intent, 81 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 82 } 83 84 private Intent getAssistIntent() { 85 Intent intent = null; 86 SearchManager searchManager = getSearchManager(); 87 if (searchManager != null) { 88 ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity(); 89 if (globalSearchActivity != null) { 90 intent = new Intent(Intent.ACTION_ASSIST); 91 intent.setPackage(globalSearchActivity.getPackageName()); 92 } else { 93 Slog.w(TAG, "No global search activity"); 94 } 95 } else { 96 Slog.w(TAG, "No SearchManager"); 97 } 98 return intent; 99 } 100 101 private SearchManager getSearchManager() { 102 if (mSearchManager == null) { 103 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 104 } 105 return mSearchManager; 106 } 107 108 private void startAssistActivity() { 109 // Close Recent Apps if needed 110 mBar.animateCollapse(); 111 // Launch Assist 112 Intent intent = getAssistIntent(); 113 try { 114 ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 115 R.anim.search_launch_enter, R.anim.search_launch_exit); 116 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 117 mContext.startActivity(intent, opts.toBundle()); 118 } catch (ActivityNotFoundException e) { 119 Slog.w(TAG, "Activity not found for " + intent.getAction()); 120 } 121 } 122 123 final MultiWaveView.OnTriggerListener mMultiWaveViewListener 124 = new MultiWaveView.OnTriggerListener() { 125 126 private int mTarget = -1; 127 128 public void onGrabbed(View v, int handle) { 129 } 130 131 public void onReleased(View v, int handle) { 132 } 133 134 public void onGrabbedStateChange(View v, int handle) { 135 if (mTarget == -1 && OnTriggerListener.NO_HANDLE == handle) { 136 mBar.hideSearchPanel(); 137 } 138 } 139 140 public void onTrigger(View v, int target) { 141 mTarget = target; 142 } 143 144 public void onFinishFinalAnimation() { 145 if (mTarget != -1) { 146 final int resId = mMultiWaveView.getResourceIdForTarget(mTarget); 147 mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger 148 switch (resId) { 149 case com.android.internal.R.drawable.ic_lockscreen_search: 150 startAssistActivity(); 151 break; 152 } 153 mBar.hideSearchPanel(); 154 } 155 } 156 }; 157 158 @Override 159 protected void onFinishInflate() { 160 super.onFinishInflate(); 161 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 162 mSearchTargetsContainer = (ViewGroup) findViewById(R.id.search_panel_container); 163 mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy); 164 // TODO: fetch views 165 mMultiWaveView = (MultiWaveView) findViewById(R.id.multi_wave_view); 166 mMultiWaveView.setOnTriggerListener(mMultiWaveViewListener); 167 } 168 169 private boolean pointInside(int x, int y, View v) { 170 final int l = v.getLeft(); 171 final int r = v.getRight(); 172 final int t = v.getTop(); 173 final int b = v.getBottom(); 174 return x >= l && x < r && y >= t && y < b; 175 } 176 177 public boolean isInContentArea(int x, int y) { 178 if (pointInside(x, y, mSearchTargetsContainer)) { 179 return true; 180 } else if (mStatusBarTouchProxy != null && 181 pointInside(x, y, mStatusBarTouchProxy)) { 182 return true; 183 } else { 184 return false; 185 } 186 } 187 188 private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { 189 public boolean onPreDraw() { 190 getViewTreeObserver().removeOnPreDrawListener(this); 191 mMultiWaveView.resumeAnimations(); 192 return false; 193 } 194 }; 195 196 public void show(final boolean show, boolean animate) { 197 if (animate) { 198 if (mShowing != show) { 199 mShowing = show; 200 // TODO: start animating ring 201 } 202 } else { 203 mShowing = show; 204 onAnimationEnd(null); 205 } 206 if (show) { 207 if (getVisibility() != View.VISIBLE) { 208 setVisibility(View.VISIBLE); 209 // Don't start the animation until we've created the layer, which is done 210 // right before we are drawn 211 mMultiWaveView.suspendAnimations(); 212 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); 213 } 214 setFocusable(true); 215 setFocusableInTouchMode(true); 216 requestFocus(); 217 } else { 218 setVisibility(View.INVISIBLE); 219 } 220 } 221 222 public void hide(boolean animate) { 223 if (mBar != null) { 224 // This will indirectly cause show(false, ...) to get called 225 mBar.animateCollapse(); 226 } else { 227 setVisibility(View.INVISIBLE); 228 } 229 } 230 231 public void onAnimationCancel(Animator animation) { 232 } 233 234 public void onAnimationEnd(Animator animation) { 235 if (mShowing) { 236 final LayoutTransition transitioner = new LayoutTransition(); 237 ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(transitioner); 238 createCustomAnimations(transitioner); 239 } else { 240 ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(null); 241 } 242 } 243 244 public void onAnimationRepeat(Animator animation) { 245 } 246 247 public void onAnimationStart(Animator animation) { 248 } 249 250 /** 251 * We need to be aligned at the bottom. LinearLayout can't do this, so instead, 252 * let LinearLayout do all the hard work, and then shift everything down to the bottom. 253 */ 254 @Override 255 protected void onLayout(boolean changed, int l, int t, int r, int b) { 256 super.onLayout(changed, l, t, r, b); 257 // setPanelHeight(mSearchTargetsContainer.getHeight()); 258 } 259 260 @Override 261 public boolean dispatchHoverEvent(MotionEvent event) { 262 // Ignore hover events outside of this panel bounds since such events 263 // generate spurious accessibility events with the panel content when 264 // tapping outside of it, thus confusing the user. 265 final int x = (int) event.getX(); 266 final int y = (int) event.getY(); 267 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { 268 return super.dispatchHoverEvent(event); 269 } 270 return true; 271 } 272 273 /** 274 * Whether the panel is showing, or, if it's animating, whether it will be 275 * when the animation is done. 276 */ 277 public boolean isShowing() { 278 return mShowing; 279 } 280 281 public void setBar(BaseStatusBar bar) { 282 mBar = bar; 283 } 284 285 public void setStatusBarView(final View statusBarView) { 286 if (mStatusBarTouchProxy != null) { 287 mStatusBarTouchProxy.setStatusBar(statusBarView); 288// mMultiWaveView.setOnTouchListener(new OnTouchListener() { 289// public boolean onTouch(View v, MotionEvent event) { 290// return statusBarView.onTouchEvent(event); 291// } 292// }); 293 } 294 } 295 296 private void createCustomAnimations(LayoutTransition transitioner) { 297 transitioner.setDuration(200); 298 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); 299 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); 300 } 301} 302