Home | History | Annotate | Download | only in systemui
      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 
     17 package com.android.systemui;
     18 
     19 import android.animation.LayoutTransition;
     20 import android.app.ActivityManagerNative;
     21 import android.app.ActivityOptions;
     22 import android.app.SearchManager;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.res.Resources;
     28 import android.os.RemoteException;
     29 import android.os.ServiceManager;
     30 import android.os.UserHandle;
     31 import android.os.Vibrator;
     32 import android.provider.Settings;
     33 import android.util.AttributeSet;
     34 import android.util.Slog;
     35 import android.view.IWindowManager;
     36 import android.view.MotionEvent;
     37 import android.view.View;
     38 import android.view.ViewGroup;
     39 import android.view.ViewTreeObserver;
     40 import android.view.ViewTreeObserver.OnPreDrawListener;
     41 import android.widget.FrameLayout;
     42 
     43 import com.android.internal.widget.multiwaveview.GlowPadView;
     44 import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
     45 import com.android.systemui.R;
     46 import com.android.systemui.recent.StatusBarTouchProxy;
     47 import com.android.systemui.statusbar.BaseStatusBar;
     48 import com.android.systemui.statusbar.CommandQueue;
     49 import com.android.systemui.statusbar.phone.PhoneStatusBar;
     50 import com.android.systemui.statusbar.tablet.StatusBarPanel;
     51 import com.android.systemui.statusbar.tablet.TabletStatusBar;
     52 
     53 public class SearchPanelView extends FrameLayout implements
     54         StatusBarPanel, ActivityOptions.OnAnimationStartedListener {
     55     private static final int SEARCH_PANEL_HOLD_DURATION = 0;
     56     static final String TAG = "SearchPanelView";
     57     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     58     private static final String ASSIST_ICON_METADATA_NAME =
     59             "com.android.systemui.action_assist_icon";
     60     private final Context mContext;
     61     private BaseStatusBar mBar;
     62     private StatusBarTouchProxy mStatusBarTouchProxy;
     63 
     64     private boolean mShowing;
     65     private View mSearchTargetsContainer;
     66     private GlowPadView mGlowPadView;
     67     private IWindowManager mWm;
     68 
     69     public SearchPanelView(Context context, AttributeSet attrs) {
     70         this(context, attrs, 0);
     71     }
     72 
     73     public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
     74         super(context, attrs, defStyle);
     75         mContext = context;
     76         mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
     77     }
     78 
     79     private void startAssistActivity() {
     80         if (!mBar.isDeviceProvisioned()) return;
     81 
     82         // Close Recent Apps if needed
     83         mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
     84         boolean isKeyguardShowing = false;
     85         try {
     86             isKeyguardShowing = mWm.isKeyguardLocked();
     87         } catch (RemoteException e) {
     88 
     89         }
     90 
     91         if (isKeyguardShowing) {
     92             // Have keyguard show the bouncer and launch the activity if the user succeeds.
     93             try {
     94                 mWm.showAssistant();
     95             } catch (RemoteException e) {
     96                 // too bad, so sad...
     97             }
     98             onAnimationStarted();
     99         } else {
    100             // Otherwise, keyguard isn't showing so launch it from here.
    101             Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
    102                     .getAssistIntent(mContext, UserHandle.USER_CURRENT);
    103             if (intent == null) return;
    104 
    105             try {
    106                 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
    107             } catch (RemoteException e) {
    108                 // too bad, so sad...
    109             }
    110 
    111             try {
    112                 ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
    113                         R.anim.search_launch_enter, R.anim.search_launch_exit,
    114                         getHandler(), this);
    115                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    116                 mContext.startActivityAsUser(intent, opts.toBundle(),
    117                         new UserHandle(UserHandle.USER_CURRENT));
    118             } catch (ActivityNotFoundException e) {
    119                 Slog.w(TAG, "Activity not found for " + intent.getAction());
    120                 onAnimationStarted();
    121             }
    122         }
    123     }
    124 
    125     class GlowPadTriggerListener implements GlowPadView.OnTriggerListener {
    126         boolean mWaitingForLaunch;
    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 (!mWaitingForLaunch && OnTriggerListener.NO_HANDLE == handle) {
    136                 mBar.hideSearchPanel();
    137             }
    138         }
    139 
    140         public void onTrigger(View v, final int target) {
    141             final int resId = mGlowPadView.getResourceIdForTarget(target);
    142             switch (resId) {
    143                 case com.android.internal.R.drawable.ic_action_assist_generic:
    144                     mWaitingForLaunch = true;
    145                     startAssistActivity();
    146                     vibrate();
    147                     break;
    148             }
    149         }
    150 
    151         public void onFinishFinalAnimation() {
    152         }
    153     }
    154     final GlowPadTriggerListener mGlowPadViewListener = new GlowPadTriggerListener();
    155 
    156     @Override
    157     public void onAnimationStarted() {
    158         postDelayed(new Runnable() {
    159             public void run() {
    160                 mGlowPadViewListener.mWaitingForLaunch = false;
    161                 mBar.hideSearchPanel();
    162             }
    163         }, SEARCH_PANEL_HOLD_DURATION);
    164     }
    165 
    166     @Override
    167     protected void onFinishInflate() {
    168         super.onFinishInflate();
    169         mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    170         mSearchTargetsContainer = findViewById(R.id.search_panel_container);
    171         mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
    172         // TODO: fetch views
    173         mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
    174         mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
    175     }
    176 
    177     private void maybeSwapSearchIcon() {
    178         Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
    179                 .getAssistIntent(mContext, UserHandle.USER_CURRENT);
    180         if (intent != null) {
    181             ComponentName component = intent.getComponent();
    182             if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
    183                     ASSIST_ICON_METADATA_NAME,
    184                     com.android.internal.R.drawable.ic_action_assist_generic)) {
    185                 if (DEBUG) Slog.v(TAG, "Couldn't grab icon for component " + component);
    186             }
    187         }
    188     }
    189 
    190     private boolean pointInside(int x, int y, View v) {
    191         final int l = v.getLeft();
    192         final int r = v.getRight();
    193         final int t = v.getTop();
    194         final int b = v.getBottom();
    195         return x >= l && x < r && y >= t && y < b;
    196     }
    197 
    198     public boolean isInContentArea(int x, int y) {
    199         if (pointInside(x, y, mSearchTargetsContainer)) {
    200             return true;
    201         } else if (mStatusBarTouchProxy != null &&
    202                 pointInside(x, y, mStatusBarTouchProxy)) {
    203             return true;
    204         } else {
    205             return false;
    206         }
    207     }
    208 
    209     private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
    210         public boolean onPreDraw() {
    211             getViewTreeObserver().removeOnPreDrawListener(this);
    212             mGlowPadView.resumeAnimations();
    213             return false;
    214         }
    215     };
    216 
    217     private void vibrate() {
    218         Context context = getContext();
    219         if (Settings.System.getIntForUser(context.getContentResolver(),
    220                 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
    221             Resources res = context.getResources();
    222             Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    223             vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
    224         }
    225     }
    226 
    227     public void show(final boolean show, boolean animate) {
    228         if (!show) {
    229             final LayoutTransition transitioner = animate ? createLayoutTransitioner() : null;
    230             ((ViewGroup) mSearchTargetsContainer).setLayoutTransition(transitioner);
    231         }
    232         mShowing = show;
    233         if (show) {
    234             maybeSwapSearchIcon();
    235             if (getVisibility() != View.VISIBLE) {
    236                 setVisibility(View.VISIBLE);
    237                 // Don't start the animation until we've created the layer, which is done
    238                 // right before we are drawn
    239                 mGlowPadView.suspendAnimations();
    240                 mGlowPadView.ping();
    241                 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
    242                 vibrate();
    243             }
    244             setFocusable(true);
    245             setFocusableInTouchMode(true);
    246             requestFocus();
    247         } else {
    248             setVisibility(View.INVISIBLE);
    249         }
    250     }
    251 
    252     public void hide(boolean animate) {
    253         if (mBar != null) {
    254             // This will indirectly cause show(false, ...) to get called
    255             mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    256         } else {
    257             setVisibility(View.INVISIBLE);
    258         }
    259     }
    260 
    261     /**
    262      * We need to be aligned at the bottom.  LinearLayout can't do this, so instead,
    263      * let LinearLayout do all the hard work, and then shift everything down to the bottom.
    264      */
    265     @Override
    266     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    267         super.onLayout(changed, l, t, r, b);
    268         // setPanelHeight(mSearchTargetsContainer.getHeight());
    269     }
    270 
    271     @Override
    272     public boolean dispatchHoverEvent(MotionEvent event) {
    273         // Ignore hover events outside of this panel bounds since such events
    274         // generate spurious accessibility events with the panel content when
    275         // tapping outside of it, thus confusing the user.
    276         final int x = (int) event.getX();
    277         final int y = (int) event.getY();
    278         if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
    279             return super.dispatchHoverEvent(event);
    280         }
    281         return true;
    282     }
    283 
    284     /**
    285      * Whether the panel is showing, or, if it's animating, whether it will be
    286      * when the animation is done.
    287      */
    288     public boolean isShowing() {
    289         return mShowing;
    290     }
    291 
    292     public void setBar(BaseStatusBar bar) {
    293         mBar = bar;
    294     }
    295 
    296     public void setStatusBarView(final View statusBarView) {
    297         if (mStatusBarTouchProxy != null) {
    298             mStatusBarTouchProxy.setStatusBar(statusBarView);
    299 //            mGlowPadView.setOnTouchListener(new OnTouchListener() {
    300 //                public boolean onTouch(View v, MotionEvent event) {
    301 //                    return statusBarView.onTouchEvent(event);
    302 //                }
    303 //            });
    304         }
    305     }
    306 
    307     private LayoutTransition createLayoutTransitioner() {
    308         LayoutTransition transitioner = new LayoutTransition();
    309         transitioner.setDuration(200);
    310         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
    311         transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
    312         return transitioner;
    313     }
    314 
    315     public boolean isAssistantAvailable() {
    316         return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
    317                 .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
    318     }
    319 }
    320