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