SearchPanelView.java revision 960892c0afa7f2b91236928e29e3987ed35b2077
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 Intent intent = getAssistIntent(); 110 try { 111 ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 112 R.anim.search_launch_enter, R.anim.search_launch_exit); 113 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 114 mContext.startActivity(intent, opts.toBundle()); 115 } catch (ActivityNotFoundException e) { 116 Slog.w(TAG, "Activity not found for " + intent.getAction()); 117 } 118 } 119 120 final MultiWaveView.OnTriggerListener mMultiWaveViewListener 121 = new MultiWaveView.OnTriggerListener() { 122 123 private int mTarget = -1; 124 125 public void onGrabbed(View v, int handle) { 126 } 127 128 public void onReleased(View v, int handle) { 129 } 130 131 public void onGrabbedStateChange(View v, int handle) { 132 if (mTarget == -1 && OnTriggerListener.NO_HANDLE == handle) { 133 mBar.hideSearchPanel(); 134 } 135 } 136 137 public void onTrigger(View v, int target) { 138 mTarget = target; 139 } 140 141 public void onFinishFinalAnimation() { 142 if (mTarget != -1) { 143 final int resId = mMultiWaveView.getResourceIdForTarget(mTarget); 144 mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger 145 switch (resId) { 146 case com.android.internal.R.drawable.ic_lockscreen_search: 147 startAssistActivity(); 148 break; 149 } 150 mBar.hideSearchPanel(); 151 } 152 } 153 }; 154 155 @Override 156 protected void onFinishInflate() { 157 super.onFinishInflate(); 158 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 159 mSearchTargetsContainer = (ViewGroup) findViewById(R.id.search_panel_container); 160 mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy); 161 // TODO: fetch views 162 mMultiWaveView = (MultiWaveView) findViewById(R.id.multi_wave_view); 163 mMultiWaveView.setOnTriggerListener(mMultiWaveViewListener); 164 } 165 166 private boolean pointInside(int x, int y, View v) { 167 final int l = v.getLeft(); 168 final int r = v.getRight(); 169 final int t = v.getTop(); 170 final int b = v.getBottom(); 171 return x >= l && x < r && y >= t && y < b; 172 } 173 174 public boolean isInContentArea(int x, int y) { 175 if (pointInside(x, y, mSearchTargetsContainer)) { 176 return true; 177 } else if (mStatusBarTouchProxy != null && 178 pointInside(x, y, mStatusBarTouchProxy)) { 179 return true; 180 } else { 181 return false; 182 } 183 } 184 185 private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { 186 public boolean onPreDraw() { 187 getViewTreeObserver().removeOnPreDrawListener(this); 188 mMultiWaveView.resumeAnimations(); 189 return false; 190 } 191 }; 192 193 public void show(final boolean show, boolean animate) { 194 if (animate) { 195 if (mShowing != show) { 196 mShowing = show; 197 // TODO: start animating ring 198 } 199 } else { 200 mShowing = show; 201 onAnimationEnd(null); 202 } 203 if (show) { 204 if (getVisibility() != View.VISIBLE) { 205 setVisibility(View.VISIBLE); 206 // Don't start the animation until we've created the layer, which is done 207 // right before we are drawn 208 mMultiWaveView.suspendAnimations(); 209 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); 210 } 211 setFocusable(true); 212 setFocusableInTouchMode(true); 213 requestFocus(); 214 } else { 215 setVisibility(View.INVISIBLE); 216 } 217 } 218 219 public void hide(boolean animate) { 220 if (mBar != null) { 221 // This will indirectly cause show(false, ...) to get called 222 mBar.animateCollapse(); 223 } else { 224 setVisibility(View.INVISIBLE); 225 } 226 } 227 228 public void onAnimationCancel(Animator animation) { 229 } 230 231 public void onAnimationEnd(Animator animation) { 232 if (mShowing) { 233 final LayoutTransition transitioner = new LayoutTransition(); 234 ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(transitioner); 235 createCustomAnimations(transitioner); 236 } else { 237 ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(null); 238 } 239 } 240 241 public void onAnimationRepeat(Animator animation) { 242 } 243 244 public void onAnimationStart(Animator animation) { 245 } 246 247 /** 248 * We need to be aligned at the bottom. LinearLayout can't do this, so instead, 249 * let LinearLayout do all the hard work, and then shift everything down to the bottom. 250 */ 251 @Override 252 protected void onLayout(boolean changed, int l, int t, int r, int b) { 253 super.onLayout(changed, l, t, r, b); 254 // setPanelHeight(mSearchTargetsContainer.getHeight()); 255 } 256 257 @Override 258 public boolean dispatchHoverEvent(MotionEvent event) { 259 // Ignore hover events outside of this panel bounds since such events 260 // generate spurious accessibility events with the panel content when 261 // tapping outside of it, thus confusing the user. 262 final int x = (int) event.getX(); 263 final int y = (int) event.getY(); 264 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { 265 return super.dispatchHoverEvent(event); 266 } 267 return true; 268 } 269 270 /** 271 * Whether the panel is showing, or, if it's animating, whether it will be 272 * when the animation is done. 273 */ 274 public boolean isShowing() { 275 return mShowing; 276 } 277 278 public void setBar(BaseStatusBar bar) { 279 mBar = bar; 280 } 281 282 public void setStatusBarView(final View statusBarView) { 283 if (mStatusBarTouchProxy != null) { 284 mStatusBarTouchProxy.setStatusBar(statusBarView); 285// mMultiWaveView.setOnTouchListener(new OnTouchListener() { 286// public boolean onTouch(View v, MotionEvent event) { 287// return statusBarView.onTouchEvent(event); 288// } 289// }); 290 } 291 } 292 293 private void createCustomAnimations(LayoutTransition transitioner) { 294 transitioner.setDuration(200); 295 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); 296 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); 297 } 298} 299