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