Since binder call permissions are not transitive by design, the proper way to fix this is to have the call talk directly to keyguard from the navigation bar. Fixes bug 9409008 Change-Id: Ibd90a79bb638c969b514455a2ad93c6ff668222d
307 lines
11 KiB
Java
307 lines
11 KiB
Java
/*
|
|
* 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.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.res.Resources;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.os.UserHandle;
|
|
import android.os.Vibrator;
|
|
import android.provider.Settings;
|
|
import android.util.AttributeSet;
|
|
import android.util.EventLog;
|
|
import android.util.Log;
|
|
import android.view.IWindowManager;
|
|
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.GlowPadView;
|
|
import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
|
|
import com.android.systemui.statusbar.BaseStatusBar;
|
|
import com.android.systemui.statusbar.CommandQueue;
|
|
import com.android.systemui.statusbar.StatusBarPanel;
|
|
import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
|
|
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
|
|
|
public class SearchPanelView extends FrameLayout implements
|
|
StatusBarPanel, ActivityOptions.OnAnimationStartedListener {
|
|
private static final int SEARCH_PANEL_HOLD_DURATION = 0;
|
|
static final String TAG = "SearchPanelView";
|
|
static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
|
|
public static final boolean DEBUG_GESTURES = true;
|
|
private static final String ASSIST_ICON_METADATA_NAME =
|
|
"com.android.systemui.action_assist_icon";
|
|
private final Context mContext;
|
|
private BaseStatusBar mBar;
|
|
|
|
private boolean mShowing;
|
|
private View mSearchTargetsContainer;
|
|
private GlowPadView mGlowPadView;
|
|
private IWindowManager mWm;
|
|
|
|
public SearchPanelView(Context context, AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
|
|
super(context, attrs, defStyle);
|
|
mContext = context;
|
|
mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
|
|
}
|
|
|
|
private void startAssistActivity() {
|
|
if (!mBar.isDeviceProvisioned()) return;
|
|
|
|
// Close Recent Apps if needed
|
|
mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
|
|
boolean isKeyguardShowing = false;
|
|
try {
|
|
isKeyguardShowing = mWm.isKeyguardLocked();
|
|
} catch (RemoteException e) {
|
|
|
|
}
|
|
|
|
if (isKeyguardShowing) {
|
|
// Have keyguard show the bouncer and launch the activity if the user succeeds.
|
|
KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
|
|
onAnimationStarted();
|
|
} else {
|
|
// Otherwise, keyguard isn't showing so launch it from here.
|
|
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
|
|
.getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
|
|
if (intent == null) return;
|
|
|
|
try {
|
|
ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
|
|
} catch (RemoteException e) {
|
|
// too bad, so sad...
|
|
}
|
|
|
|
try {
|
|
ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
|
|
R.anim.search_launch_enter, R.anim.search_launch_exit,
|
|
getHandler(), this);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
mContext.startActivityAsUser(intent, opts.toBundle(),
|
|
new UserHandle(UserHandle.USER_CURRENT));
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "Activity not found for " + intent.getAction());
|
|
onAnimationStarted();
|
|
}
|
|
}
|
|
}
|
|
|
|
class GlowPadTriggerListener implements GlowPadView.OnTriggerListener {
|
|
boolean mWaitingForLaunch;
|
|
|
|
public void onGrabbed(View v, int handle) {
|
|
}
|
|
|
|
public void onReleased(View v, int handle) {
|
|
}
|
|
|
|
public void onGrabbedStateChange(View v, int handle) {
|
|
if (!mWaitingForLaunch && OnTriggerListener.NO_HANDLE == handle) {
|
|
mBar.hideSearchPanel();
|
|
}
|
|
}
|
|
|
|
public void onTrigger(View v, final int target) {
|
|
final int resId = mGlowPadView.getResourceIdForTarget(target);
|
|
switch (resId) {
|
|
case com.android.internal.R.drawable.ic_action_assist_generic:
|
|
mWaitingForLaunch = true;
|
|
startAssistActivity();
|
|
vibrate();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void onFinishFinalAnimation() {
|
|
}
|
|
}
|
|
final GlowPadTriggerListener mGlowPadViewListener = new GlowPadTriggerListener();
|
|
|
|
@Override
|
|
public void onAnimationStarted() {
|
|
postDelayed(new Runnable() {
|
|
public void run() {
|
|
mGlowPadViewListener.mWaitingForLaunch = false;
|
|
mBar.hideSearchPanel();
|
|
}
|
|
}, SEARCH_PANEL_HOLD_DURATION);
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
super.onFinishInflate();
|
|
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
mSearchTargetsContainer = findViewById(R.id.search_panel_container);
|
|
// TODO: fetch views
|
|
mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
|
|
mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
|
|
}
|
|
|
|
private void maybeSwapSearchIcon() {
|
|
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
|
|
.getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
|
|
if (intent != null) {
|
|
ComponentName component = intent.getComponent();
|
|
if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
|
|
ASSIST_ICON_METADATA_NAME,
|
|
com.android.internal.R.drawable.ic_action_assist_generic)) {
|
|
if (DEBUG) Log.v(TAG, "Couldn't grab icon for component " + component);
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
return pointInside(x, y, mSearchTargetsContainer);
|
|
}
|
|
|
|
private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
|
|
public boolean onPreDraw() {
|
|
getViewTreeObserver().removeOnPreDrawListener(this);
|
|
mGlowPadView.resumeAnimations();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
private void vibrate() {
|
|
Context context = getContext();
|
|
if (Settings.System.getIntForUser(context.getContentResolver(),
|
|
Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
|
|
Resources res = context.getResources();
|
|
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
|
vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
|
|
}
|
|
}
|
|
|
|
public void show(final boolean show, boolean animate) {
|
|
if (!show) {
|
|
final LayoutTransition transitioner = animate ? createLayoutTransitioner() : null;
|
|
((ViewGroup) mSearchTargetsContainer).setLayoutTransition(transitioner);
|
|
}
|
|
mShowing = show;
|
|
if (show) {
|
|
maybeSwapSearchIcon();
|
|
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
|
|
mGlowPadView.suspendAnimations();
|
|
mGlowPadView.ping();
|
|
getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
|
|
vibrate();
|
|
}
|
|
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.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
|
|
} else {
|
|
setVisibility(View.INVISIBLE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
if (DEBUG_GESTURES) {
|
|
if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
|
|
EventLog.writeEvent(EventLogTags.SYSUI_SEARCHPANEL_TOUCH,
|
|
event.getActionMasked(), (int) event.getX(), (int) event.getY());
|
|
}
|
|
}
|
|
return super.onTouchEvent(event);
|
|
}
|
|
|
|
private LayoutTransition createLayoutTransitioner() {
|
|
LayoutTransition transitioner = new LayoutTransition();
|
|
transitioner.setDuration(200);
|
|
transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
|
|
transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
|
|
return transitioner;
|
|
}
|
|
|
|
public boolean isAssistantAvailable() {
|
|
return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
|
|
.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
|
|
}
|
|
}
|