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