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