/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui; import android.animation.Animator; import android.animation.LayoutTransition; import android.app.ActivityManagerNative; import android.app.ActivityOptions; import android.app.SearchManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.FrameLayout; import com.android.internal.widget.multiwaveview.MultiWaveView; import com.android.internal.widget.multiwaveview.MultiWaveView.OnTriggerListener; import com.android.server.am.ActivityManagerService; import com.android.systemui.R; import com.android.systemui.recent.StatusBarTouchProxy; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.tablet.StatusBarPanel; import com.android.systemui.statusbar.tablet.TabletStatusBar; public class SearchPanelView extends FrameLayout implements StatusBarPanel, Animator.AnimatorListener { static final String TAG = "SearchPanelView"; static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; private Context mContext; private BaseStatusBar mBar; private StatusBarTouchProxy mStatusBarTouchProxy; private boolean mShowing; private View mSearchTargetsContainer; private MultiWaveView mMultiWaveView; public SearchPanelView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SearchPanelView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); if (mSearchManager == null) { Slog.w(TAG, "Search manager not available"); } } private SearchManager mSearchManager; // This code should be the same as that used in LockScreen.java public boolean isAssistantAvailable() { Intent intent = getAssistIntent(); return intent == null ? false : mContext.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } private Intent getAssistIntent() { Intent intent = null; SearchManager searchManager = getSearchManager(); if (searchManager != null) { ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity(); if (globalSearchActivity != null) { intent = new Intent(Intent.ACTION_ASSIST); intent.setPackage(globalSearchActivity.getPackageName()); } else { Slog.w(TAG, "No global search activity"); } } else { Slog.w(TAG, "No SearchManager"); } return intent; } private SearchManager getSearchManager() { if (mSearchManager == null) { mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); } return mSearchManager; } private void startAssistActivity() { Intent intent = getAssistIntent(); try { ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, R.anim.search_launch_enter, R.anim.search_launch_exit); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent, opts.toBundle()); } catch (ActivityNotFoundException e) { Slog.w(TAG, "Activity not found for " + intent.getAction()); } } final MultiWaveView.OnTriggerListener mMultiWaveViewListener = new MultiWaveView.OnTriggerListener() { private int mTarget = -1; public void onGrabbed(View v, int handle) { } public void onReleased(View v, int handle) { } public void onGrabbedStateChange(View v, int handle) { if (OnTriggerListener.NO_HANDLE == handle) { mBar.hideSearchPanel(); } } public void onTrigger(View v, int target) { mTarget = target; } public void onFinishFinalAnimation() { if (mTarget != -1) { final int resId = mMultiWaveView.getResourceIdForTarget(mTarget); mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger switch (resId) { case com.android.internal.R.drawable.ic_lockscreen_search: startAssistActivity(); break; } } mBar.hideSearchPanel(); } }; @Override protected void onFinishInflate() { super.onFinishInflate(); mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mSearchTargetsContainer = (ViewGroup) findViewById(R.id.search_panel_container); mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy); // TODO: fetch views mMultiWaveView = (MultiWaveView) findViewById(R.id.multi_wave_view); mMultiWaveView.setOnTriggerListener(mMultiWaveViewListener); } private boolean pointInside(int x, int y, View v) { final int l = v.getLeft(); final int r = v.getRight(); final int t = v.getTop(); final int b = v.getBottom(); return x >= l && x < r && y >= t && y < b; } public boolean isInContentArea(int x, int y) { if (pointInside(x, y, mSearchTargetsContainer)) { return true; } else if (mStatusBarTouchProxy != null && pointInside(x, y, mStatusBarTouchProxy)) { return true; } else { return false; } } private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); mMultiWaveView.resumeAnimations(); return false; } }; public void show(final boolean show, boolean animate) { if (animate) { if (mShowing != show) { mShowing = show; // TODO: start animating ring } } else { mShowing = show; onAnimationEnd(null); } if (show) { if (getVisibility() != View.VISIBLE) { setVisibility(View.VISIBLE); // Don't start the animation until we've created the layer, which is done // right before we are drawn mMultiWaveView.suspendAnimations(); getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); } setFocusable(true); setFocusableInTouchMode(true); requestFocus(); } else { setVisibility(View.INVISIBLE); } } public void hide(boolean animate) { if (mBar != null) { // This will indirectly cause show(false, ...) to get called mBar.animateCollapse(); } else { setVisibility(View.INVISIBLE); } } public void onAnimationCancel(Animator animation) { } public void onAnimationEnd(Animator animation) { if (mShowing) { final LayoutTransition transitioner = new LayoutTransition(); ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(transitioner); createCustomAnimations(transitioner); } else { ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(null); } } public void onAnimationRepeat(Animator animation) { } public void onAnimationStart(Animator animation) { } /** * We need to be aligned at the bottom. LinearLayout can't do this, so instead, * let LinearLayout do all the hard work, and then shift everything down to the bottom. */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // setPanelHeight(mSearchTargetsContainer.getHeight()); } @Override public boolean dispatchHoverEvent(MotionEvent event) { // Ignore hover events outside of this panel bounds since such events // generate spurious accessibility events with the panel content when // tapping outside of it, thus confusing the user. final int x = (int) event.getX(); final int y = (int) event.getY(); if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { return super.dispatchHoverEvent(event); } return true; } /** * Whether the panel is showing, or, if it's animating, whether it will be * when the animation is done. */ public boolean isShowing() { return mShowing; } public void setBar(BaseStatusBar bar) { mBar = bar; } public void setStatusBarView(final View statusBarView) { if (mStatusBarTouchProxy != null) { mStatusBarTouchProxy.setStatusBar(statusBarView); // mMultiWaveView.setOnTouchListener(new OnTouchListener() { // public boolean onTouch(View v, MotionEvent event) { // return statusBarView.onTouchEvent(event); // } // }); } } private void createCustomAnimations(LayoutTransition transitioner) { transitioner.setDuration(200); transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); } }