diff options
Diffstat (limited to 'policy')
19 files changed, 2866 insertions, 1107 deletions
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java index 0ce4b12..bfbd60d 100644 --- a/policy/src/com/android/internal/policy/impl/BarController.java +++ b/policy/src/com/android/internal/policy/impl/BarController.java @@ -108,13 +108,20 @@ public class BarController { if (mWin != null) { if (win != null && (win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) { - if ((win.getAttrs().flags & mTranslucentWmFlag) != 0) { + int fl = PolicyControl.getWindowFlags(win, null); + if ((fl & mTranslucentWmFlag) != 0) { vis |= mTranslucentFlag; } else { vis &= ~mTranslucentFlag; } + if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + vis |= View.SYSTEM_UI_TRANSPARENT; + } else { + vis &= ~View.SYSTEM_UI_TRANSPARENT; + } } else { vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag); + vis = (vis & ~View.SYSTEM_UI_TRANSPARENT) | (oldVis & View.SYSTEM_UI_TRANSPARENT); } } return vis; @@ -230,7 +237,8 @@ public class BarController { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile } - if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0) { + if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 || + ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) { mLastTranslucent = SystemClock.uptimeMillis(); } return vis; diff --git a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java index 71b0d53..6f79f58 100644 --- a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java +++ b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java @@ -83,6 +83,7 @@ public class EnableAccessibilityController { private final Context mContext; + private final Runnable mOnAccessibilityEnabledCallback; private final UserManager mUserManager; private final TextToSpeech mTts; private final Ringtone mTone; @@ -97,8 +98,9 @@ public class EnableAccessibilityController { private float mSecondPointerDownX; private float mSecondPointerDownY; - public EnableAccessibilityController(Context context) { + public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) { mContext = context; + mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback; mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() { @Override @@ -275,5 +277,7 @@ public class EnableAccessibilityController { /* ignore */ } } + + mOnAccessibilityEnabledCallback.run(); } } diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 1a25d90..dfcdaa0 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -21,6 +21,7 @@ import com.android.internal.app.AlertController.AlertParams; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.R; +import com.android.internal.widget.LockPatternUtils; import android.app.ActivityManagerNative; import android.app.AlertDialog; @@ -35,6 +36,7 @@ import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.ConnectivityManager; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -51,6 +53,7 @@ import android.service.dreams.IDreamManager; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.TypedValue; @@ -62,7 +65,10 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerInternal; import android.view.WindowManagerPolicy.WindowManagerFuncs; +import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; @@ -92,6 +98,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private static final String GLOBAL_ACTION_KEY_SILENT = "silent"; private static final String GLOBAL_ACTION_KEY_USERS = "users"; private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings"; + private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown"; private final Context mContext; private final WindowManagerFuncs mWindowManagerFuncs; @@ -185,7 +192,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac // If we only have 1 item and it's a simple press action, just do this action. if (mAdapter.getCount() == 1 - && mAdapter.getItem(0) instanceof SinglePressAction) { + && mAdapter.getItem(0) instanceof SinglePressAction + && !(mAdapter.getItem(0) instanceof LongPressAction)) { ((SinglePressAction) mAdapter.getItem(0)).onPress(); } else { WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes(); @@ -262,20 +270,26 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac continue; } if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) { - mItems.add(getPowerAction()); + mItems.add(new PowerAction()); } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) { mItems.add(mAirplaneModeOn); - } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey) - && (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner())) { - mItems.add(getBugReportAction()); - } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey) && mShowSilentToggle) { - mItems.add(mSilentModeAction); - } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey) - && SystemProperties.getBoolean("fw.power_user_switcher", false)) { - addUsersToMenu(mItems); + } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) { + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) { + mItems.add(getBugReportAction()); + } + } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) { + if (mShowSilentToggle) { + mItems.add(mSilentModeAction); + } + } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) { + if (SystemProperties.getBoolean("fw.power_user_switcher", false)) { + addUsersToMenu(mItems); + } } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) { mItems.add(getSettingsAction()); + } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) { + mItems.add(getLockdownAction()); } else { Log.e(TAG, "Invalid global action key " + actionKey); } @@ -300,7 +314,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - return mAdapter.getItem(position).onLongPress(); + final Action action = mAdapter.getItem(position); + if (action instanceof LongPressAction) { + return ((LongPressAction) action).onLongPress(); + } + return false; } }); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); @@ -310,34 +328,38 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac return dialog; } - private Action getPowerAction() { - return new SinglePressAction( - com.android.internal.R.drawable.ic_lock_power_off, - R.string.global_action_power_off) { + private final class PowerAction extends SinglePressAction implements LongPressAction { + private PowerAction() { + super(com.android.internal.R.drawable.ic_lock_power_off, + R.string.global_action_power_off); + } - public void onPress() { - // shutdown by making sure radio and power are handled accordingly. - mWindowManagerFuncs.shutdown(true); - } + @Override + public boolean onLongPress() { + mWindowManagerFuncs.rebootSafeMode(true); + return true; + } - public boolean onLongPress() { - mWindowManagerFuncs.rebootSafeMode(true); - return true; - } + @Override + public boolean showDuringKeyguard() { + return true; + } - public boolean showDuringKeyguard() { - return true; - } + @Override + public boolean showBeforeProvisioning() { + return true; + } - public boolean showBeforeProvisioning() { - return true; - } - }; + @Override + public void onPress() { + // shutdown by making sure radio and power are handled accordingly. + mWindowManagerFuncs.shutdown(false /* confirm */); + } } private Action getBugReportAction() { - return new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb, - R.string.global_action_bug_report) { + return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport, + R.string.bugreport_title) { public void onPress() { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); @@ -367,10 +389,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac dialog.show(); } - public boolean onLongPress() { - return false; - } - public boolean showDuringKeyguard() { return true; } @@ -378,6 +396,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac public boolean showBeforeProvisioning() { return false; } + + @Override + public String getStatus() { + return mContext.getString( + com.android.internal.R.string.bugreport_status, + Build.VERSION.RELEASE, + Build.ID); + } }; } @@ -393,8 +419,29 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } @Override - public boolean onLongPress() { - return false; + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + }; + } + + private Action getLockdownAction() { + return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock, + R.string.global_action_lockdown) { + + @Override + public void onPress() { + new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL); + try { + WindowManagerGlobal.getWindowManagerService().lockNow(null); + } catch (RemoteException e) { + Log.e(TAG, "Error while trying to lock device.", e); + } } @Override @@ -404,7 +451,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac @Override public boolean showBeforeProvisioning() { - return true; + return false; } }; } @@ -423,36 +470,38 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } private void addUsersToMenu(ArrayList<Action> items) { - List<UserInfo> users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) - .getUsers(); - if (users.size() > 1) { + UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + if (um.isUserSwitcherEnabled()) { + List<UserInfo> users = um.getUsers(); UserInfo currentUser = getCurrentUser(); for (final UserInfo user : users) { - boolean isCurrentUser = currentUser == null - ? user.id == 0 : (currentUser.id == user.id); - Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath) - : null; - SinglePressAction switchToUser = new SinglePressAction( - com.android.internal.R.drawable.ic_menu_cc, icon, - (user.name != null ? user.name : "Primary") - + (isCurrentUser ? " \u2714" : "")) { - public void onPress() { - try { - ActivityManagerNative.getDefault().switchUser(user.id); - } catch (RemoteException re) { - Log.e(TAG, "Couldn't switch user " + re); + if (user.supportsSwitchTo()) { + boolean isCurrentUser = currentUser == null + ? user.id == 0 : (currentUser.id == user.id); + Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath) + : null; + SinglePressAction switchToUser = new SinglePressAction( + com.android.internal.R.drawable.ic_menu_cc, icon, + (user.name != null ? user.name : "Primary") + + (isCurrentUser ? " \u2714" : "")) { + public void onPress() { + try { + ActivityManagerNative.getDefault().switchUser(user.id); + } catch (RemoteException re) { + Log.e(TAG, "Couldn't switch user " + re); + } } - } - public boolean showDuringKeyguard() { - return true; - } + public boolean showDuringKeyguard() { + return true; + } - public boolean showBeforeProvisioning() { - return false; - } - }; - items.add(switchToUser); + public boolean showBeforeProvisioning() { + return false; + } + }; + items.add(switchToUser); + } } } } @@ -577,12 +626,16 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac * What each item in the global actions dialog must be able to support. */ private interface Action { + /** + * @return Text that will be announced when dialog is created. null + * for none. + */ + CharSequence getLabelForAccessibility(Context context); + View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater); void onPress(); - public boolean onLongPress(); - /** * @return whether this action should appear in the dialog when the keygaurd * is showing. @@ -599,6 +652,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } /** + * An action that also supports long press. + */ + private interface LongPressAction extends Action { + boolean onLongPress(); + } + + /** * A single press action maintains no state, just responds to a press * and takes an action. */ @@ -633,10 +693,18 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac return true; } + public String getStatus() { + return null; + } + abstract public void onPress(); - public boolean onLongPress() { - return false; + public CharSequence getLabelForAccessibility(Context context) { + if (mMessage != null) { + return mMessage; + } else { + return context.getString(mMessageResId); + } } public View create( @@ -646,12 +714,18 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac ImageView icon = (ImageView) v.findViewById(R.id.icon); TextView messageView = (TextView) v.findViewById(R.id.message); - v.findViewById(R.id.status).setVisibility(View.GONE); + TextView statusView = (TextView) v.findViewById(R.id.status); + final String status = getStatus(); + if (!TextUtils.isEmpty(status)) { + statusView.setText(status); + } else { + statusView.setVisibility(View.GONE); + } if (mIcon != null) { icon.setImageDrawable(mIcon); icon.setScaleType(ScaleType.CENTER_CROP); } else if (mIconResId != 0) { - icon.setImageDrawable(context.getResources().getDrawable(mIconResId)); + icon.setImageDrawable(context.getDrawable(mIconResId)); } if (mMessage != null) { messageView.setText(mMessage); @@ -722,6 +796,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } + @Override + public CharSequence getLabelForAccessibility(Context context) { + return context.getString(mMessageResId); + } + public View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { willCreate(); @@ -741,7 +820,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac boolean on = ((mState == State.On) || (mState == State.TurningOn)); if (icon != null) { - icon.setImageDrawable(context.getResources().getDrawable( + icon.setImageDrawable(context.getDrawable( (on ? mEnabledIconResId : mDisabledIconResid))); icon.setEnabled(enabled); } @@ -767,10 +846,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac changeStateFromPress(nowOn); } - public boolean onLongPress() { - return false; - } - public boolean isEnabled() { return !mState.inTransition(); } @@ -842,6 +917,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac return index; } + @Override + public CharSequence getLabelForAccessibility(Context context) { + return null; + } + public View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false); @@ -860,10 +940,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac public void onPress() { } - public boolean onLongPress() { - return false; - } - public boolean showDuringKeyguard() { return true; } @@ -994,6 +1070,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private final Context mContext; private final int mWindowTouchSlop; private final AlertController mAlert; + private final MyAdapter mAdapter; private EnableAccessibilityController mEnableAccessibilityController; @@ -1004,6 +1081,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac super(context, getDialogTheme(context)); mContext = context; mAlert = new AlertController(mContext, this, getWindow()); + mAdapter = (MyAdapter) params.mAdapter; mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop(); params.apply(mAlert); } @@ -1022,7 +1100,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac // is dismissed on the first down while the global gesture is a long press // with two fingers anywhere on the screen. if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) { - mEnableAccessibilityController = new EnableAccessibilityController(mContext); + mEnableAccessibilityController = new EnableAccessibilityController(mContext, + new Runnable() { + @Override + public void run() { + dismiss(); + } + }); super.setCanceledOnTouchOutside(false); } else { mEnableAccessibilityController = null; @@ -1092,6 +1176,20 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + for (int i = 0; i < mAdapter.getCount(); ++i) { + CharSequence label = + mAdapter.getItem(i).getLabelForAccessibility(getContext()); + if (label != null) { + event.getText().add(label); + } + } + } + return super.dispatchPopulateAccessibilityEvent(event); + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (mAlert.onKeyDown(keyCode, event)) { return true; diff --git a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java index 3cf7e82..8c8209f 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java +++ b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java @@ -30,6 +30,7 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.PrintWriter; /** * Stores a mapping of global keys. @@ -123,4 +124,21 @@ final class GlobalKeyManager { } } } + + public void dump(String prefix, PrintWriter pw) { + final int numKeys = mKeyMapping.size(); + if (numKeys == 0) { + pw.print(prefix); pw.println("mKeyMapping.size=0"); + return; + } + pw.print(prefix); pw.println("mKeyMapping={"); + for (int i = 0; i < numKeys; ++i) { + pw.print(" "); + pw.print(prefix); + pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i))); + pw.print("="); + pw.println(mKeyMapping.valueAt(i).flattenToString()); + } + pw.print(prefix); pw.println("}"); + } } diff --git a/policy/src/com/android/internal/policy/impl/IconUtilities.java b/policy/src/com/android/internal/policy/impl/IconUtilities.java index a47c904..82f26ad 100644 --- a/policy/src/com/android/internal/policy/impl/IconUtilities.java +++ b/policy/src/com/android/internal/policy/impl/IconUtilities.java @@ -24,22 +24,13 @@ import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; -import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.TableMaskFilter; -import android.graphics.Typeface; -import android.text.Layout.Alignment; -import android.text.StaticLayout; -import android.text.TextPaint; import android.util.DisplayMetrics; -import android.util.Log; import android.util.TypedValue; -import android.view.ContextThemeWrapper; import android.content.res.Resources; import android.content.Context; diff --git a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java index b734c41..8fc4647 100644 --- a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java +++ b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java @@ -19,6 +19,7 @@ package com.android.internal.policy.impl; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -27,12 +28,12 @@ import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; -import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Slog; +import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -46,8 +47,6 @@ import android.widget.FrameLayout; import com.android.internal.R; -import java.util.Arrays; - /** * Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden * entering immersive mode. @@ -56,19 +55,19 @@ public class ImmersiveModeConfirmation { private static final String TAG = "ImmersiveModeConfirmation"; private static final boolean DEBUG = false; private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution + private static final String CONFIRMED = "confirmed"; private final Context mContext; private final H mHandler; - private final ArraySet<String> mConfirmedPackages = new ArraySet<String>(); private final long mShowDelayMs; private final long mPanicThresholdMs; + private final SparseBooleanArray mUserPanicResets = new SparseBooleanArray(); + private boolean mConfirmed; private ClingWindowView mClingWindow; - private String mLastPackage; - private String mPromptPackage; private long mPanicTime; - private String mPanicPackage; private WindowManager mWindowManager; + private int mCurrentUserId; public ImmersiveModeConfirmation(Context context) { mContext = context; @@ -85,83 +84,85 @@ public class ImmersiveModeConfirmation { return exit != null ? exit.getDuration() : 0; } - public void loadSetting() { - if (DEBUG) Slog.d(TAG, "loadSetting()"); - mConfirmedPackages.clear(); - String packages = null; + public void loadSetting(int currentUserId) { + mConfirmed = false; + mCurrentUserId = currentUserId; + if (DEBUG) Slog.d(TAG, String.format("loadSetting() mCurrentUserId=%d resetForPanic=%s", + mCurrentUserId, mUserPanicResets.get(mCurrentUserId, false))); + String value = null; try { - packages = Settings.Secure.getStringForUser(mContext.getContentResolver(), + value = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, UserHandle.USER_CURRENT); - if (packages != null) { - mConfirmedPackages.addAll(Arrays.asList(packages.split(","))); - if (DEBUG) Slog.d(TAG, "Loaded mConfirmedPackages=" + mConfirmedPackages); - } + mConfirmed = CONFIRMED.equals(value); + if (DEBUG) Slog.d(TAG, "Loaded mConfirmed=" + mConfirmed); } catch (Throwable t) { - Slog.w(TAG, "Error loading confirmations, packages=" + packages, t); + Slog.w(TAG, "Error loading confirmations, value=" + value, t); } } private void saveSetting() { if (DEBUG) Slog.d(TAG, "saveSetting()"); try { - final String packages = TextUtils.join(",", mConfirmedPackages); + final String value = mConfirmed ? CONFIRMED : null; Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, - packages, + value, UserHandle.USER_CURRENT); - if (DEBUG) Slog.d(TAG, "Saved packages=" + packages); + if (DEBUG) Slog.d(TAG, "Saved value=" + value); } catch (Throwable t) { - Slog.w(TAG, "Error saving confirmations, mConfirmedPackages=" + mConfirmedPackages, t); + Slog.w(TAG, "Error saving confirmations, mConfirmed=" + mConfirmed, t); } } - public void immersiveModeChanged(String pkg, boolean isImmersiveMode) { - if (pkg == null) { - return; - } + public void immersiveModeChanged(String pkg, boolean isImmersiveMode, + boolean userSetupComplete) { mHandler.removeMessages(H.SHOW); if (isImmersiveMode) { - mLastPackage = pkg; - if (DEBUG_SHOW_EVERY_TIME || !mConfirmedPackages.contains(pkg)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(H.SHOW, pkg), mShowDelayMs); + final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg); + if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s", + disabled, mConfirmed)); + if (!disabled && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) && userSetupComplete) { + mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs); } } else { - mLastPackage = null; mHandler.sendEmptyMessage(H.HIDE); } } - public void onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode) { - if (mPanicPackage != null && !isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { + public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode) { + if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { // turning the screen back on within the panic threshold - unconfirmPackage(mPanicPackage); + mHandler.sendEmptyMessage(H.PANIC); + return mClingWindow == null; } if (isScreenOn && inImmersiveMode) { // turning the screen off, remember if we were in immersive mode mPanicTime = time; - mPanicPackage = mLastPackage; } else { mPanicTime = 0; - mPanicPackage = null; } + return false; } public void confirmCurrentPrompt() { - mHandler.post(confirmAction(mPromptPackage)); + if (mClingWindow != null) { + if (DEBUG) Slog.d(TAG, "confirmCurrentPrompt()"); + mHandler.post(mConfirm); + } } - private void unconfirmPackage(String pkg) { - if (pkg != null) { - if (DEBUG) Slog.d(TAG, "Unconfirming immersive mode confirmation for " + pkg); - mConfirmedPackages.remove(pkg); - saveSetting(); - } + private void handlePanic() { + if (DEBUG) Slog.d(TAG, "handlePanic()"); + if (mUserPanicResets.get(mCurrentUserId, false)) return; // already reset for panic + mUserPanicResets.put(mCurrentUserId, true); + mConfirmed = false; + saveSetting(); } private void handleHide() { if (mClingWindow != null) { - if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation for " + mPromptPackage); + if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation"); mWindowManager.removeView(mClingWindow); mClingWindow = null; } @@ -292,16 +293,14 @@ public class ImmersiveModeConfirmation { @Override public boolean onTouchEvent(MotionEvent motion) { - Slog.v(TAG, "ClingWindowView.onTouchEvent"); return true; } } - private void handleShow(String pkg) { - mPromptPackage = pkg; - if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation for " + pkg); + private void handleShow() { + if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation"); - mClingWindow = new ClingWindowView(mContext, confirmAction(pkg)); + mClingWindow = new ClingWindowView(mContext, mConfirm); // we will be hiding the nav bar, so layout as if it's already hidden mClingWindow.setSystemUiVisibility( @@ -313,33 +312,35 @@ public class ImmersiveModeConfirmation { mWindowManager.addView(mClingWindow, lp); } - private Runnable confirmAction(final String pkg) { - return new Runnable() { - @Override - public void run() { - if (pkg != null && !mConfirmedPackages.contains(pkg)) { - if (DEBUG) Slog.d(TAG, "Confirming immersive mode for " + pkg); - mConfirmedPackages.add(pkg); - saveSetting(); - } - handleHide(); + private final Runnable mConfirm = new Runnable() { + @Override + public void run() { + if (DEBUG) Slog.d(TAG, "mConfirm.run()"); + if (!mConfirmed) { + mConfirmed = true; + saveSetting(); } - }; - } + handleHide(); + } + }; private final class H extends Handler { - private static final int SHOW = 0; - private static final int HIDE = 1; + private static final int SHOW = 1; + private static final int HIDE = 2; + private static final int PANIC = 3; @Override public void handleMessage(Message msg) { switch(msg.what) { case SHOW: - handleShow((String)msg.obj); + handleShow(); break; case HIDE: handleHide(); break; + case PANIC: + handlePanic(); + break; } } } diff --git a/policy/src/com/android/internal/policy/impl/LogDecelerateInterpolator.java b/policy/src/com/android/internal/policy/impl/LogDecelerateInterpolator.java new file mode 100644 index 0000000..1f3e1de --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/LogDecelerateInterpolator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.internal.policy.impl; + +import android.view.animation.Interpolator; + +public class LogDecelerateInterpolator implements Interpolator { + + private int mBase; + private int mDrift; + private final float mLogScale; + + public LogDecelerateInterpolator(int base, int drift) { + mBase = base; + mDrift = drift; + + mLogScale = 1f / computeLog(1, mBase, mDrift); + } + + private static float computeLog(float t, int base, int drift) { + return (float) -Math.pow(base, -t) + 1 + (drift * t); + } + + @Override + public float getInterpolation(float t) { + return computeLog(t, mBase, mDrift) * mLogScale; + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java index 417527c..f291e89 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java +++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.media.AudioManager; import android.media.IAudioService; +import android.media.session.MediaSessionLegacyHelper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -70,19 +71,19 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { return onKeyUp(keyCode, event); } } - + boolean onKeyDown(int keyCode, KeyEvent event) { /* **************************************************************************** * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. * See the comment in PhoneWindow.onKeyDown * ****************************************************************************/ final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); - + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { - getAudioManager().handleKeyDown(event, AudioManager.USE_DEFAULT_STREAM_TYPE); + MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); return true; } @@ -156,7 +157,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { if (event.getRepeatCount() == 0) { dispatcher.startTracking(event, this); } else if (event.isLongPress() && dispatcher.isTracking(event)) { - Configuration config = mContext.getResources().getConfiguration(); + Configuration config = mContext.getResources().getConfiguration(); if (config.keyboard == Configuration.KEYBOARD_NOKEYS || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { // launch the search activity @@ -191,17 +192,13 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { if (dispatcher != null) { dispatcher.handleUpEvent(event); } - + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { if (!event.isCanceled()) { - AudioManager audioManager = (AudioManager)mContext.getSystemService( - Context.AUDIO_SERVICE); - if (audioManager != null) { - getAudioManager().handleKeyUp(event, AudioManager.USE_DEFAULT_STREAM_TYPE); - } + MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); } return true; } @@ -277,30 +274,20 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { } return mKeyguardManager; } - + AudioManager getAudioManager() { if (mAudioManager == null) { mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); } return mAudioManager; } - + void sendCloseSystemWindows() { PhoneWindowManager.sendCloseSystemWindows(mContext, null); } private void handleMediaKeyEvent(KeyEvent keyEvent) { - IAudioService audioService = IAudioService.Stub.asInterface( - ServiceManager.checkService(Context.AUDIO_SERVICE)); - if (audioService != null) { - try { - audioService.dispatchMediaKeyEvent(keyEvent); - } catch (RemoteException e) { - Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e); - } - } else { - Slog.w(TAG, "Unable to find IAudioService for media key event."); - } + MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java b/policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java index 968976b..df6fca4c 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java +++ b/policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java @@ -16,8 +16,6 @@ package com.android.internal.policy.impl; -import java.util.Map; - import android.content.Context; import android.util.AttributeSet; import android.view.View; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index d68e351..5f3b877 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.*; -import android.view.ViewConfiguration; - import com.android.internal.R; import com.android.internal.view.RootViewSurfaceTaker; import com.android.internal.view.StandaloneActionMode; @@ -34,37 +32,42 @@ import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuDialogHelper; import com.android.internal.view.menu.MenuPresenter; import com.android.internal.view.menu.MenuView; -import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; -import com.android.internal.widget.ActionBarOverlayLayout; -import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.BackgroundFallback; +import com.android.internal.widget.DecorContentParent; import com.android.internal.widget.SwipeDismissLayout; +import android.app.ActivityManager; import android.app.KeyguardManager; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; -import android.os.Debug; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.transition.Scene; +import android.transition.Transition; +import android.transition.TransitionInflater; +import android.transition.TransitionManager; +import android.transition.TransitionSet; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; @@ -83,6 +86,7 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewManager; import android.view.ViewParent; @@ -116,6 +120,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private final static boolean SWEEP_OPEN_MENU = false; + private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300; + + private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES | + (1 << FEATURE_CUSTOM_TITLE) | + (1 << FEATURE_CONTENT_TRANSITIONS) | + (1 << FEATURE_ACTIVITY_TRANSITIONS) | + (1 << FEATURE_ACTION_MODE_OVERLAY); + + private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet(); + /** * Simple callback used by the context menu and its submenus. The options * menu submenus do not use this (their behavior is more complex). @@ -137,20 +151,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent; + private ViewGroup mContentRoot; + SurfaceHolder.Callback2 mTakeSurfaceCallback; - + InputQueue.Callback mTakeInputQueueCallback; - + private boolean mIsFloating; private LayoutInflater mLayoutInflater; private TextView mTitleView; - - private ActionBarView mActionBar; + + private DecorContentParent mDecorContentParent; private ActionMenuPresenterCallback mActionMenuPresenterCallback; private PanelMenuPresenterCallback mPanelMenuPresenterCallback; + private TransitionManager mTransitionManager; + private Scene mContentScene; + // The icon resource has been explicitly set elsewhere // and should not be overwritten with a default. static final int FLAG_RESOURCE_SET_ICON = 1 << 0; @@ -193,24 +212,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ProgressBar mHorizontalProgressBar; private int mBackgroundResource = 0; + private int mBackgroundFallbackResource = 0; private Drawable mBackgroundDrawable; + private float mElevation; + + /** Whether window content should be clipped to the background outline. */ + private boolean mClipToOutline; + private int mFrameResource = 0; private int mTextColor = 0; + private int mStatusBarColor = 0; + private int mNavigationBarColor = 0; + private boolean mForcedStatusBarColor = false; + private boolean mForcedNavigationBarColor = false; private CharSequence mTitle = null; private int mTitleColor = 0; private boolean mAlwaysReadCloseOnTouchAttr = false; - + private ContextMenuBuilder mContextMenu; private MenuDialogHelper mContextMenuHelper; private boolean mClosingActionMenu; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; + private MediaController mMediaController; private AudioManager mAudioManager; private KeyguardManager mKeyguardManager; @@ -231,6 +261,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } }; + private Transition mEnterTransition = null; + private Transition mReturnTransition = USE_DEFAULT_TRANSITION; + private Transition mExitTransition = null; + private Transition mReenterTransition = USE_DEFAULT_TRANSITION; + private Transition mSharedElementEnterTransition = null; + private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION; + private Transition mSharedElementExitTransition = null; + private Transition mSharedElementReenterTransition = USE_DEFAULT_TRANSITION; + private Boolean mAllowReturnTransitionOverlap; + private Boolean mAllowEnterTransitionOverlap; + private long mBackgroundFadeDurationMillis = -1; + private Boolean mSharedElementsUseOverlay; + + private Rect mTempRect; + static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); @@ -254,16 +299,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { throw new AndroidRuntimeException("requestFeature() must be called before adding content"); } final int features = getFeatures(); - if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) { - - /* Another feature is enabled and the user is trying to enable the custom title feature */ - throw new AndroidRuntimeException("You cannot combine custom titles with other title features"); - } - if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) && - (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) { - - /* Custom title feature is enabled and the user is trying to enable another feature */ - throw new AndroidRuntimeException("You cannot combine custom titles with other title features"); + final int newFeatures = features | (1 << featureId); + if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 && + (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) { + // Another feature is enabled and the user is trying to enable the custom title feature + // or custom title feature is enabled and the user is trying to enable another feature + throw new AndroidRuntimeException( + "You cannot combine custom titles with other title features"); } if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) { return false; // Ignore. No title dominates. @@ -300,13 +342,38 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override + public TransitionManager getTransitionManager() { + return mTransitionManager; + } + + @Override + public void setTransitionManager(TransitionManager tm) { + mTransitionManager = tm; + } + + @Override + public Scene getContentScene() { + return mContentScene; + } + + @Override public void setContentView(int layoutResID) { + // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window + // decor, when theme attributes and the like are crystalized. Do not check the feature + // before this happens. if (mContentParent == null) { installDecor(); - } else { + } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } - mLayoutInflater.inflate(layoutResID, mContentParent); + + if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { + final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, + getContext()); + transitionTo(newScene); + } else { + mLayoutInflater.inflate(layoutResID, mContentParent); + } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); @@ -320,12 +387,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setContentView(View view, ViewGroup.LayoutParams params) { + // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window + // decor, when theme attributes and the like are crystalized. Do not check the feature + // before this happens. if (mContentParent == null) { installDecor(); - } else { + } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } - mContentParent.addView(view, params); + + if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { + view.setLayoutParams(params); + final Scene newScene = new Scene(mContentParent, view); + transitionTo(newScene); + } else { + mContentParent.addView(view, params); + } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); @@ -337,6 +414,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null) { installDecor(); } + if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { + // TODO Augment the scenes/transitions API to support this. + Log.v(TAG, "addContentView does not support content transitions"); + } mContentParent.addView(view, params); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { @@ -344,6 +425,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + private void transitionTo(Scene scene) { + if (mContentScene == null) { + scene.enter(); + } else { + mTransitionManager.transitionTo(scene); + } + mContentScene = scene; + } + @Override public View getCurrentFocus() { return mDecor != null ? mDecor.findFocus() : null; @@ -353,11 +443,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public void takeSurface(SurfaceHolder.Callback2 callback) { mTakeSurfaceCallback = callback; } - + public void takeInputQueue(InputQueue.Callback callback) { mTakeInputQueueCallback = callback; } - + @Override public boolean isFloating() { return mIsFloating; @@ -378,13 +468,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public void setTitle(CharSequence title) { if (mTitleView != null) { mTitleView.setText(title); - } else if (mActionBar != null) { - mActionBar.setWindowTitle(title); + } else if (mDecorContentParent != null) { + mDecorContentParent.setWindowTitle(title); } mTitle = title; } @Override + @Deprecated public void setTitleColor(int textColor) { if (mTitleView != null) { mTitleView.setTextColor(textColor); @@ -410,7 +501,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (st.isPrepared) { return true; } - + if ((mPreparedPanel != null) && (mPreparedPanel != st)) { // Another Panel is prepared and possibly open, so close it closePanel(mPreparedPanel, false); @@ -425,10 +516,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final boolean isActionBarMenu = (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR); - if (isActionBarMenu && mActionBar != null) { + if (isActionBarMenu && mDecorContentParent != null) { // Enforce ordering guarantees around events so that the action bar never // dispatches menu-related events before the panel is prepared. - mActionBar.setMenuPrepared(); + mDecorContentParent.setMenuPrepared(); } if (st.createdPanelView == null) { @@ -440,11 +531,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - if (isActionBarMenu && mActionBar != null) { + if (isActionBarMenu && mDecorContentParent != null) { if (mActionMenuPresenterCallback == null) { mActionMenuPresenterCallback = new ActionMenuPresenterCallback(); } - mActionBar.setMenu(st.menu, mActionMenuPresenterCallback); + mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback); } // Call callback, and return if it doesn't want to display menu. @@ -456,14 +547,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Ditch the menu created above st.setMenu(null); - if (isActionBarMenu && mActionBar != null) { + if (isActionBarMenu && mDecorContentParent != null) { // Don't show it in the action bar either - mActionBar.setMenu(null, mActionMenuPresenterCallback); + mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); } return false; } - + st.refreshMenuContent = false; } @@ -481,10 +572,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) { - if (isActionBarMenu && mActionBar != null) { + if (isActionBarMenu && mDecorContentParent != null) { // The app didn't want to show the menu for now but it still exists. // Clear it out of the action bar. - mActionBar.setMenu(null, mActionMenuPresenterCallback); + mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); } st.menu.startDispatchingItemsChanged(); return false; @@ -509,7 +600,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void onConfigurationChanged(Configuration newConfig) { // Action bars handle their own menu state - if (mActionBar == null) { + if (mDecorContentParent == null) { PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); if ((st != null) && (st.menu != null)) { if (st.isOpen) { @@ -555,18 +646,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Causes the decor view to be recreated st.refreshDecorView = true; - + st.clearMenuPresenters(); } @Override public final void openPanel(int featureId, KeyEvent event) { - if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && - mActionBar.isOverflowReserved() && + if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && + mDecorContentParent.canShowOverflowMenu() && !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { - if (mActionBar.getVisibility() == View.VISIBLE) { - mActionBar.showOverflowMenu(); - } + mDecorContentParent.showOverflowMenu(); } else { openPanel(getPanelState(featureId, true), event); } @@ -643,7 +732,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Otherwise, set the normal panel background backgroundResId = st.background; } - st.decorView.setWindowBackground(getContext().getResources().getDrawable( + st.decorView.setWindowBackground(getContext().getDrawable( backgroundResId)); ViewParent shownPanelParent = st.shownPanelView.getParent(); @@ -695,10 +784,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public final void closePanel(int featureId) { - if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && - mActionBar.isOverflowReserved() && + if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && + mDecorContentParent.canShowOverflowMenu() && !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { - mActionBar.hideOverflowMenu(); + mDecorContentParent.hideOverflowMenu(); } else if (featureId == FEATURE_CONTEXT_MENU) { closeContextMenu(); } else { @@ -720,7 +809,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public final void closePanel(PanelFeatureState st, boolean doCallback) { // System.out.println("Close panel: isOpen=" + st.isOpen); if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL && - mActionBar != null && mActionBar.isOverflowMenuShowing()) { + mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) { checkCloseActionMenu(st.menu); return; } @@ -766,7 +855,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } mClosingActionMenu = true; - mActionBar.dismissPopupMenus(); + mDecorContentParent.dismissPopups(); Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onPanelClosed(FEATURE_ACTION_BAR, menu); @@ -794,6 +883,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + void doPendingInvalidatePanelMenu() { + if (mInvalidatePanelMenuPosted) { + mDecor.removeCallbacks(mInvalidatePanelMenuRunnable); + mInvalidatePanelMenuRunnable.run(); + } + } + void doInvalidatePanelMenu(int featureId) { PanelFeatureState st = getPanelState(featureId, true); Bundle savedActionViewStates = null; @@ -809,10 +905,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } st.refreshMenuContent = true; st.refreshDecorView = true; - + // Prepare the options panel if we have an action bar if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL) - && mActionBar != null) { + && mDecorContentParent != null) { st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false); if (st != null) { st.isPrepared = false; @@ -820,7 +916,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } } - + /** * Called when the panel key is pushed down. * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}. @@ -829,7 +925,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { */ public final boolean onKeyDownPanel(int featureId, KeyEvent event) { final int keyCode = event.getKeyCode(); - + if (event.getRepeatCount() == 0) { // The panel key was pushed, so set the chording key mPanelChordingKey = keyCode; @@ -856,20 +952,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) { return; } - + boolean playSoundEffect = false; final PanelFeatureState st = getPanelState(featureId, true); - if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && - mActionBar.isOverflowReserved() && + if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && + mDecorContentParent.canShowOverflowMenu() && !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { - if (mActionBar.getVisibility() == View.VISIBLE) { - if (!mActionBar.isOverflowMenuShowing()) { - if (!isDestroyed() && preparePanel(st, event)) { - playSoundEffect = mActionBar.showOverflowMenu(); - } - } else { - playSoundEffect = mActionBar.hideOverflowMenu(); + if (!mDecorContentParent.isOverflowMenuShowing()) { + if (!isDestroyed() && preparePanel(st, event)) { + playSoundEffect = mDecorContentParent.showOverflowMenu(); } + } else { + playSoundEffect = mDecorContentParent.hideOverflowMenu(); } } else { if (st.isOpen || st.isHandled) { @@ -982,7 +1076,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { st.isHandled = true; // Only close down the menu if we don't have an action bar keeping it open. - if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mActionBar == null) { + if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) { closePanel(st, true); } } @@ -1004,7 +1098,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean res = st.menu.performIdentifierAction(id, flags); // Only close down the menu if we don't have an action bar keeping it open. - if (mActionBar == null) { + if (mDecorContentParent == null) { closePanel(st, true); } @@ -1039,12 +1133,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } private void reopenMenu(boolean toggleMenuMode) { - if (mActionBar != null && mActionBar.isOverflowReserved() && + if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() && (!ViewConfiguration.get(getContext()).hasPermanentMenuKey() || - mActionBar.isOverflowMenuShowPending())) { + mDecorContentParent.isOverflowMenuShowPending())) { final Callback cb = getCallback(); - if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) { - if (cb != null && !isDestroyed() && mActionBar.getVisibility() == View.VISIBLE) { + if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) { + if (cb != null && !isDestroyed()) { // If we have a menu invalidation pending, do it now. if (mInvalidatePanelMenuPosted && (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) { @@ -1059,11 +1153,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (st.menu != null && !st.refreshMenuContent && cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); - mActionBar.showOverflowMenu(); + mDecorContentParent.showOverflowMenu(); } } } else { - mActionBar.hideOverflowMenu(); + mDecorContentParent.hideOverflowMenu(); if (cb != null && !isDestroyed()) { final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu); @@ -1098,22 +1192,40 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected boolean initializePanelMenu(final PanelFeatureState st) { Context context = getContext(); - // If we have an action bar, initialize the menu with a context themed for it. + // If we have an action bar, initialize the menu with the right theme. if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) && - mActionBar != null) { - TypedValue outValue = new TypedValue(); - Resources.Theme currentTheme = context.getTheme(); - currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme, - outValue, true); - final int targetThemeRes = outValue.resourceId; + mDecorContentParent != null) { + final TypedValue outValue = new TypedValue(); + final Theme baseTheme = context.getTheme(); + baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); + + Theme widgetTheme = null; + if (outValue.resourceId != 0) { + widgetTheme = context.getResources().newTheme(); + widgetTheme.setTo(baseTheme); + widgetTheme.applyStyle(outValue.resourceId, true); + widgetTheme.resolveAttribute( + R.attr.actionBarWidgetTheme, outValue, true); + } else { + baseTheme.resolveAttribute( + R.attr.actionBarWidgetTheme, outValue, true); + } + + if (outValue.resourceId != 0) { + if (widgetTheme == null) { + widgetTheme = context.getResources().newTheme(); + widgetTheme.setTo(baseTheme); + } + widgetTheme.applyStyle(outValue.resourceId, true); + } - if (targetThemeRes != 0 && context.getThemeResId() != targetThemeRes) { - context = new ContextThemeWrapper(context, targetThemeRes); + if (widgetTheme != null) { + context = new ContextThemeWrapper(context, 0); + context.getTheme().setTo(widgetTheme); } } final MenuBuilder menu = new MenuBuilder(context); - menu.setCallback(this); st.setMenu(menu); @@ -1131,6 +1243,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { st.decorView = new DecorView(getContext(), st.featureId); st.gravity = Gravity.CENTER | Gravity.BOTTOM; st.setStyle(getContext()); + TypedArray a = getContext().obtainStyledAttributes(null, + R.styleable.Window, 0, st.listPresenterTheme); + final float elevation = a.getDimension(R.styleable.Window_windowElevation, 0); + if (elevation != 0) { + st.decorView.setElevation(elevation); + } + a.recycle(); return true; } @@ -1218,6 +1337,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mDecor != null) { mDecor.setWindowBackground(drawable); } + if (mBackgroundFallbackResource != 0) { + mDecor.setBackgroundFallback(drawable != null ? 0 : mBackgroundFallbackResource); + } } } @@ -1228,7 +1350,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (st.resid != resId) { st.resid = resId; st.uri = null; - st.local = getContext().getResources().getDrawable(resId); + st.local = getContext().getDrawable(resId); updateDrawable(featureId, st, false); } } else { @@ -1340,7 +1462,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) { updateProgressBars(value); } else if (featureId == FEATURE_CUSTOM_TITLE) { - FrameLayout titleContainer = (FrameLayout) findViewById(com.android.internal.R.id.title_container); + FrameLayout titleContainer = (FrameLayout) findViewById(R.id.title_container); if (titleContainer != null) { mLayoutInflater.inflate(value, titleContainer); } @@ -1373,30 +1495,58 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final int features = getLocalFeatures(); if (value == PROGRESS_VISIBILITY_ON) { if ((features & (1 << FEATURE_PROGRESS)) != 0) { - int level = horizontalProgressBar.getProgress(); - int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? - View.VISIBLE : View.INVISIBLE; - horizontalProgressBar.setVisibility(visibility); + if (horizontalProgressBar != null) { + int level = horizontalProgressBar.getProgress(); + int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? + View.VISIBLE : View.INVISIBLE; + horizontalProgressBar.setVisibility(visibility); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { - circularProgressBar.setVisibility(View.VISIBLE); + if (circularProgressBar != null) { + circularProgressBar.setVisibility(View.VISIBLE); + } else { + Log.e(TAG, "Circular progress bar not located in current window decor"); + } } } else if (value == PROGRESS_VISIBILITY_OFF) { if ((features & (1 << FEATURE_PROGRESS)) != 0) { - horizontalProgressBar.setVisibility(View.GONE); + if (horizontalProgressBar != null) { + horizontalProgressBar.setVisibility(View.GONE); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { - circularProgressBar.setVisibility(View.GONE); + if (circularProgressBar != null) { + circularProgressBar.setVisibility(View.GONE); + } else { + Log.e(TAG, "Circular progress bar not located in current window decor"); + } } } else if (value == PROGRESS_INDETERMINATE_ON) { - horizontalProgressBar.setIndeterminate(true); + if (horizontalProgressBar != null) { + horizontalProgressBar.setIndeterminate(true); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } } else if (value == PROGRESS_INDETERMINATE_OFF) { - horizontalProgressBar.setIndeterminate(false); + if (horizontalProgressBar != null) { + horizontalProgressBar.setIndeterminate(false); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } } else if (PROGRESS_START <= value && value <= PROGRESS_END) { // We want to set the progress value before testing for visibility // so that when the progress bar becomes visible again, it has the // correct level. - horizontalProgressBar.setProgress(value - PROGRESS_START); + if (horizontalProgressBar != null) { + horizontalProgressBar.setProgress(value - PROGRESS_START); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } if (value < PROGRESS_END) { showProgressBars(horizontalProgressBar, circularProgressBar); @@ -1404,7 +1554,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { hideProgressBars(horizontalProgressBar, circularProgressBar); } } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) { - horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START); + if (horizontalProgressBar != null) { + horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START); + } else { + Log.e(TAG, "Horizontal progress bar not located in current window decor"); + } showProgressBars(horizontalProgressBar, circularProgressBar); } @@ -1414,11 +1568,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { final int features = getLocalFeatures(); if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && - spinnyProgressBar.getVisibility() == View.INVISIBLE) { + spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.INVISIBLE) { spinnyProgressBar.setVisibility(View.VISIBLE); } // Only show the progress bars if the primary progress is not complete - if ((features & (1 << FEATURE_PROGRESS)) != 0 && + if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null && horizontalProgressBar.getProgress() < 10000) { horizontalProgressBar.setVisibility(View.VISIBLE); } @@ -1426,14 +1580,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { final int features = getLocalFeatures(); - Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out); + Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out); anim.setDuration(1000); if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && + spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.VISIBLE) { spinnyProgressBar.startAnimation(anim); spinnyProgressBar.setVisibility(View.INVISIBLE); } - if ((features & (1 << FEATURE_PROGRESS)) != 0 && + if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null && horizontalProgressBar.getVisibility() == View.VISIBLE) { horizontalProgressBar.startAnimation(anim); horizontalProgressBar.setVisibility(View.INVISIBLE); @@ -1445,8 +1600,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mIconRes = resId; mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON; mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK; - if (mActionBar != null) { - mActionBar.setIcon(resId); + if (mDecorContentParent != null) { + mDecorContentParent.setIcon(resId); } } @@ -1456,13 +1611,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return; } mIconRes = resId; - if (mActionBar != null && (!mActionBar.hasIcon() || + if (mDecorContentParent != null && (!mDecorContentParent.hasIcon() || (mResourcesSetFlags & FLAG_RESOURCE_SET_ICON_FALLBACK) != 0)) { if (resId != 0) { - mActionBar.setIcon(resId); + mDecorContentParent.setIcon(resId); mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK; } else { - mActionBar.setIcon(getContext().getPackageManager().getDefaultActivityIcon()); + mDecorContentParent.setIcon( + getContext().getPackageManager().getDefaultActivityIcon()); mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; } } @@ -1472,8 +1628,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public void setLogo(int resId) { mLogoRes = resId; mResourcesSetFlags |= FLAG_RESOURCE_SET_LOGO; - if (mActionBar != null) { - mActionBar.setLogo(resId); + if (mDecorContentParent != null) { + mDecorContentParent.setLogo(resId); } } @@ -1483,8 +1639,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return; } mLogoRes = resId; - if (mActionBar != null && !mActionBar.hasLogo()) { - mActionBar.setLogo(resId); + if (mDecorContentParent != null && !mDecorContentParent.hasLogo()) { + mDecorContentParent.setLogo(resId); } } @@ -1570,18 +1726,47 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mDecor != null ? mDecor.getKeyDispatcherState() : null; //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount() // + " flags=0x" + Integer.toHexString(event.getFlags())); - + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_DOWN: { + int direction = keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE + : AudioManager.ADJUST_LOWER; + // If we have a session send it the volume command, otherwise + // use the suggested stream. + if (mMediaController != null) { + mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI); + } else { + MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( + mVolumeControlStreamType, direction, + AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE); + } + return true; + } case KeyEvent.KEYCODE_VOLUME_MUTE: { - // Similar code is in PhoneFallbackEventHandler in case the window - // doesn't have one of these. In this case, we execute it here and - // eat the event instead, because we have mVolumeControlStreamType - // and they don't. getAudioManager().handleKeyDown(event, mVolumeControlStreamType); return true; } + // These are all the recognized media key codes in + // KeyEvent.isMediaKey() + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if (mMediaController != null) { + if (mMediaController.dispatchMediaButtonEvent(event)) { + return true; + } + } + return false; + } case KeyEvent.KEYCODE_MENU: { onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event); @@ -1632,10 +1817,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount() // + " flags=0x" + Integer.toHexString(event.getFlags())); - + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_DOWN: { + // If we have a session send it the volume command, otherwise + // use the suggested stream. + if (mMediaController != null) { + mMediaController.adjustVolume(0, AudioManager.FLAG_PLAY_SOUND + | AudioManager.FLAG_VIBRATE); + } else { + MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( + mVolumeControlStreamType, 0, + AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE); + } + return true; + } case KeyEvent.KEYCODE_VOLUME_MUTE: { // Similar code is in PhoneFallbackEventHandler in case the window // doesn't have one of these. In this case, we execute it here and @@ -1644,6 +1841,26 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { getAudioManager().handleKeyUp(event, mVolumeControlStreamType); return true; } + // These are all the recognized media key codes in + // KeyEvent.isMediaKey() + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if (mMediaController != null) { + if (mMediaController.dispatchMediaButtonEvent(event)) { + return true; + } + } + return false; + } case KeyEvent.KEYCODE_MENU: { onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId, @@ -1741,9 +1958,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { outState.putSparseParcelableArray(PANELS_TAG, panelStates); } - if (mActionBar != null) { + if (mDecorContentParent != null) { SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>(); - mActionBar.saveHierarchyState(actionBarStates); + mDecorContentParent.saveToolbarHierarchyState(actionBarStates); outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates); } @@ -1782,11 +1999,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { restorePanelState(panelStates); } - if (mActionBar != null) { + if (mDecorContentParent != null) { SparseArray<Parcelable> actionBarStates = savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG); if (actionBarStates != null) { - mActionBar.restoreHierarchyState(actionBarStates); + doPendingInvalidatePanelMenu(); + mDecorContentParent.restoreToolbarHierarchyState(actionBarStates); } else { Log.w(TAG, "Missing saved instance states for action bar views! " + "State will not be restored."); @@ -1946,11 +2164,31 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // View added at runtime to draw under the navigation bar area private View mNavigationGuard; + private View mStatusColorView; + private View mNavigationColorView; + private final BackgroundFallback mBackgroundFallback = new BackgroundFallback(); + + private int mLastTopInset = 0; + private int mLastBottomInset = 0; + private int mLastRightInset = 0; + + public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; } + public void setBackgroundFallback(int resId) { + mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null); + setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback()); + } + + @Override + public void onDraw(Canvas c) { + super.onDraw(c); + mBackgroundFallback.draw(mContentRoot, c, mContentParent); + } + @Override public boolean dispatchKeyEvent(KeyEvent event) { final int keyCode = event.getKeyCode(); @@ -2051,12 +2289,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } public boolean superDispatchKeyEvent(KeyEvent event) { - if (super.dispatchKeyEvent(event)) { - return true; - } - - // Not handled by the view hierarchy, does the action bar want it - // to cancel out of something special? + // Give priority to closing action modes if applicable. if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { final int action = event.getAction(); // Back cancels action modes first. @@ -2066,17 +2299,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } return true; } - - // Next collapse any expanded action views. - if (mActionBar != null && mActionBar.hasExpandedActionView()) { - if (action == KeyEvent.ACTION_UP) { - mActionBar.collapseActionView(); - } - return true; - } } - return false; + return super.dispatchKeyEvent(event); } public boolean superDispatchKeyShortcutEvent(KeyEvent event) { @@ -2202,7 +2427,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (!AccessibilityManager.getInstance(mContext).isEnabled()) { return; } - + // if we are showing a feature that should be announced and one child // make this child the event source since this is the feature itself // otherwise the callback will take over and announce its client @@ -2259,8 +2484,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mMenuBackground == null && mFeatureId < 0 && getAttributes().height == WindowManager.LayoutParams.MATCH_PARENT) { - mMenuBackground = getContext().getResources().getDrawable( - com.android.internal.R.drawable.menu_background); + mMenuBackground = getContext().getDrawable( + R.drawable.menu_background); } if (mMenuBackground != null) { mMenuBackground.setBounds(drawingBounds.left, @@ -2276,6 +2501,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; + final int widthMode = getMode(widthMeasureSpec); final int heightMode = getMode(heightMeasureSpec); @@ -2322,17 +2548,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mOutsetBottom != null) { int mode = MeasureSpec.getMode(heightMeasureSpec); - if (mode != MeasureSpec.UNSPECIFIED && !isPortrait) { + if (mode != MeasureSpec.UNSPECIFIED) { int outset = (int) mOutsetBottom.getDimension(metrics); int height = MeasureSpec.getSize(heightMeasureSpec); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + outset, mode); } - mode = MeasureSpec.getMode(widthMeasureSpec); - if (mode != MeasureSpec.UNSPECIFIED && isPortrait) { - int outset = (int) mOutsetBottom.getDimension(metrics); - int width = MeasureSpec.getSize(widthMeasureSpec); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(width + outset, mode); - } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -2429,19 +2649,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } else { if (mActionModeView == null) { if (isFloating()) { - mActionModeView = new ActionBarContextView(mContext); - mActionModePopup = new PopupWindow(mContext, null, - com.android.internal.R.attr.actionModePopupWindowStyle); + // Use the action bar theme. + final TypedValue outValue = new TypedValue(); + final Theme baseTheme = mContext.getTheme(); + baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); + + final Context actionBarContext; + if (outValue.resourceId != 0) { + final Theme actionBarTheme = mContext.getResources().newTheme(); + actionBarTheme.setTo(baseTheme); + actionBarTheme.applyStyle(outValue.resourceId, true); + + actionBarContext = new ContextThemeWrapper(mContext, 0); + actionBarContext.getTheme().setTo(actionBarTheme); + } else { + actionBarContext = mContext; + } + + mActionModeView = new ActionBarContextView(actionBarContext); + mActionModePopup = new PopupWindow(actionBarContext, null, + R.attr.actionModePopupWindowStyle); mActionModePopup.setWindowLayoutType( WindowManager.LayoutParams.TYPE_APPLICATION); mActionModePopup.setContentView(mActionModeView); mActionModePopup.setWidth(MATCH_PARENT); - TypedValue heightValue = new TypedValue(); - mContext.getTheme().resolveAttribute( - com.android.internal.R.attr.actionBarSize, heightValue, true); - final int height = TypedValue.complexToDimensionPixelSize(heightValue.data, - mContext.getResources().getDisplayMetrics()); + actionBarContext.getTheme().resolveAttribute( + R.attr.actionBarSize, outValue, true); + final int height = TypedValue.complexToDimensionPixelSize(outValue.data, + actionBarContext.getResources().getDisplayMetrics()); mActionModeView.setContentHeight(height); mActionModePopup.setHeight(WRAP_CONTENT); mShowActionModePopup = new Runnable() { @@ -2453,7 +2689,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { }; } else { ViewStub stub = (ViewStub) findViewById( - com.android.internal.R.id.action_mode_bar_stub); + R.id.action_mode_bar_stub); if (stub != null) { mActionModeView = (ActionBarContextView) stub.inflate(); } @@ -2462,8 +2698,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mActionModeView != null) { mActionModeView.killMode(); - mode = new StandaloneActionMode(getContext(), mActionModeView, wrappedCallback, - mActionModePopup == null); + mode = new StandaloneActionMode(mActionModeView.getContext(), mActionModeView, + wrappedCallback, mActionModePopup == null); if (callback.onCreateActionMode(mode, mode.getMenu())) { mode.invalidate(); mActionModeView.initForMode(mode); @@ -2531,48 +2767,180 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - protected boolean fitSystemWindows(Rect insets) { - mFrameOffsets.set(insets); - updateStatusGuard(insets); + public void onWindowSystemUiVisibilityChanged(int visible) { + updateColorViews(null /* insets */); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + mFrameOffsets.set(insets.getSystemWindowInsets()); + insets = updateColorViews(insets); + insets = updateStatusGuard(insets); updateNavigationGuard(insets); if (getForeground() != null) { drawableChanged(); } - return super.fitSystemWindows(insets); + return insets; } - private void updateStatusGuard(Rect insets) { + @Override + public boolean isTransitionGroup() { + return false; + } + + private WindowInsets updateColorViews(WindowInsets insets) { + WindowManager.LayoutParams attrs = getAttributes(); + int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); + + if (!mIsFloating && ActivityManager.isHighEndGfx()) { + if (insets != null) { + mLastTopInset = Math.min(insets.getStableInsetTop(), + insets.getSystemWindowInsetTop()); + mLastBottomInset = Math.min(insets.getStableInsetBottom(), + insets.getSystemWindowInsetBottom()); + mLastRightInset = Math.min(insets.getStableInsetRight(), + insets.getSystemWindowInsetRight()); + } + mStatusColorView = updateColorViewInt(mStatusColorView, sysUiVisibility, + SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, + mStatusBarColor, mLastTopInset, Gravity.TOP, + STATUS_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.statusBarBackground, + (getAttributes().flags & FLAG_FULLSCREEN) != 0); + mNavigationColorView = updateColorViewInt(mNavigationColorView, sysUiVisibility, + SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, + mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM, + NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.navigationBarBackground, + false /* hiddenByWindowFlag */); + } + + // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need + // to ensure that the rest of the view hierarchy doesn't notice it, unless they've + // explicitly asked for it. + + boolean consumingNavBar = + (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 + && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + + int consumedRight = consumingNavBar ? mLastRightInset : 0; + int consumedBottom = consumingNavBar ? mLastBottomInset : 0; + + if (mContentRoot != null + && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { + MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams(); + if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) { + lp.rightMargin = consumedRight; + lp.bottomMargin = consumedBottom; + mContentRoot.setLayoutParams(lp); + + if (insets == null) { + // The insets have changed, but we're not currently in the process + // of dispatching them. + requestApplyInsets(); + } + } + if (insets != null) { + insets = insets.replaceSystemWindowInsets( + insets.getSystemWindowInsetLeft(), + insets.getSystemWindowInsetTop(), + insets.getSystemWindowInsetRight() - consumedRight, + insets.getSystemWindowInsetBottom() - consumedBottom); + } + } + + if (insets != null) { + insets = insets.consumeStableInsets(); + } + return insets; + } + + private View updateColorViewInt(View view, int sysUiVis, int systemUiHideFlag, + int translucentFlag, int color, int height, int verticalGravity, + String transitionName, int id, boolean hiddenByWindowFlag) { + boolean show = height > 0 && (sysUiVis & systemUiHideFlag) == 0 + && !hiddenByWindowFlag + && (getAttributes().flags & translucentFlag) == 0 + && (color & Color.BLACK) != 0 + && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + + if (view == null) { + if (show) { + view = new View(mContext); + view.setBackgroundColor(color); + view.setTransitionName(transitionName); + view.setId(id); + addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height, + Gravity.START | verticalGravity)); + } + } else { + int vis = show ? VISIBLE : INVISIBLE; + view.setVisibility(vis); + if (show) { + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + if (lp.height != height) { + lp.height = height; + view.setLayoutParams(lp); + } + view.setBackgroundColor(color); + } + } + return view; + } + + private WindowInsets updateStatusGuard(WindowInsets insets) { boolean showStatusGuard = false; // Show the status guard when the non-overlay contextual action bar is showing if (mActionModeView != null) { if (mActionModeView.getLayoutParams() instanceof MarginLayoutParams) { - MarginLayoutParams mlp = (MarginLayoutParams) mActionModeView.getLayoutParams(); + // Insets are magic! + final MarginLayoutParams mlp = (MarginLayoutParams) + mActionModeView.getLayoutParams(); boolean mlpChanged = false; - final boolean nonOverlayShown = - (getLocalFeatures() & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0 - && mActionModeView.isShown(); - if (nonOverlayShown) { - // set top margin to top insets, show status guard - if (mlp.topMargin != insets.top) { + if (mActionModeView.isShown()) { + if (mTempRect == null) { + mTempRect = new Rect(); + } + final Rect rect = mTempRect; + + // If the parent doesn't consume the insets, manually + // apply the default system window insets. + mContentParent.computeSystemWindowInsets(insets, rect); + final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0; + if (mlp.topMargin != newMargin) { mlpChanged = true; - mlp.topMargin = insets.top; + mlp.topMargin = insets.getSystemWindowInsetTop(); + if (mStatusGuard == null) { mStatusGuard = new View(mContext); mStatusGuard.setBackgroundColor(mContext.getResources() .getColor(R.color.input_method_navigation_guard)); - addView(mStatusGuard, new LayoutParams( - LayoutParams.MATCH_PARENT, mlp.topMargin, - Gravity.START | Gravity.TOP)); + addView(mStatusGuard, indexOfChild(mStatusColorView), + new LayoutParams(LayoutParams.MATCH_PARENT, + mlp.topMargin, Gravity.START | Gravity.TOP)); } else { - LayoutParams lp = (LayoutParams) mStatusGuard.getLayoutParams(); + final LayoutParams lp = (LayoutParams) + mStatusGuard.getLayoutParams(); if (lp.height != mlp.topMargin) { lp.height = mlp.topMargin; mStatusGuard.setLayoutParams(lp); } } } - insets.top = 0; // consume top insets - showStatusGuard = true; + + // The action mode's theme may differ from the app, so + // always show the status guard above it if we have one. + showStatusGuard = mStatusGuard != null; + + // We only need to consume the insets if the action + // mode is overlaid on the app content (e.g. it's + // sitting in a FrameLayout, see + // screen_simple_overlay_action_mode.xml). + final boolean nonOverlay = (getLocalFeatures() + & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0; + insets = insets.consumeSystemWindowInsets( + false, nonOverlay && showStatusGuard /* top */, false, false); } else { // reset top margin if (mlp.topMargin != 0) { @@ -2588,9 +2956,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mStatusGuard != null) { mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE); } + return insets; } - private void updateNavigationGuard(Rect insets) { + private void updateNavigationGuard(WindowInsets insets) { // IMEs lay out below the nav bar, but the content view must not (for back compat) if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // prevent the content view from including the nav bar height @@ -2598,7 +2967,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams mlp = (MarginLayoutParams) mContentParent.getLayoutParams(); - mlp.bottomMargin = insets.bottom; + mlp.bottomMargin = insets.getSystemWindowInsetBottom(); mContentParent.setLayoutParams(mlp); } } @@ -2607,12 +2976,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mNavigationGuard = new View(mContext); mNavigationGuard.setBackgroundColor(mContext.getResources() .getColor(R.color.input_method_navigation_guard)); - addView(mNavigationGuard, new LayoutParams( - LayoutParams.MATCH_PARENT, insets.bottom, + addView(mNavigationGuard, indexOfChild(mNavigationColorView), new LayoutParams( + LayoutParams.MATCH_PARENT, insets.getSystemWindowInsetBottom(), Gravity.START | Gravity.BOTTOM)); } else { LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams(); - lp.height = insets.bottom; + lp.height = insets.getSystemWindowInsetBottom(); mNavigationGuard.setLayoutParams(lp); } } @@ -2630,7 +2999,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { invalidate(); int opacity = PixelFormat.OPAQUE; - // Note: if there is no background, we will assume opaque. The // common case seems to be that an application sets there to be // no background so it can draw everything itself. For that, @@ -2700,13 +3068,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity() != PixelFormat.OPAQUE); } - + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - + updateWindowResizeState(); - + final Callback cb = getCallback(); if (cb != null && !isDestroyed() && mFeatureId < 0) { cb.onAttachedToWindow(); @@ -2727,14 +3095,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - + final Callback cb = getCallback(); if (cb != null && mFeatureId < 0) { cb.onDetachedFromWindow(); } - if (mActionBar != null) { - mActionBar.dismissPopupMenus(); + if (mDecorContentParent != null) { + mDecorContentParent.dismissPopups(); } if (mActionModePopup != null) { @@ -2761,19 +3129,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { return mFeatureId < 0 ? mTakeSurfaceCallback : null; } - + public InputQueue.Callback willYouTakeTheInputQueue() { return mFeatureId < 0 ? mTakeInputQueueCallback : null; } - + public void setSurfaceType(int type) { PhoneWindow.this.setType(type); } - + public void setSurfaceFormat(int format) { PhoneWindow.this.setFormat(format); } - + public void setSurfaceKeepScreenOn(boolean keepOn) { if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -2853,14 +3221,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (false) { System.out.println("From style:"); String s = "Attrs:"; - for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) { - s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "=" + for (int i = 0; i < R.styleable.Window.length; i++) { + s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "=" + a.getString(i); } System.out.println(s); } - mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); + mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false); int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) & (~getForcedWindowFlags()); if (mIsFloating) { @@ -2870,86 +3238,94 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { + if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); - } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) { + } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) { + if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) { + if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowSwipeToDismiss, false)) { + if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) { + if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus, + if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, false)) { setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS & (~getForcedWindowFlags())); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation, + if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation, false)) { setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION & (~getForcedWindowFlags())); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) { + if (a.getBoolean(R.styleable.Window_windowOverscan, false)) { setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags())); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { + if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); } - if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch, + if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch, getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB)) { setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags())); } - a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); - a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); - if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) { + a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); + a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); + if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) { if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); - a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor, + a.getValue(R.styleable.Window_windowFixedWidthMajor, mFixedWidthMajor); } - if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) { + if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) { if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); - a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor, + a.getValue(R.styleable.Window_windowFixedWidthMinor, mFixedWidthMinor); } - if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) { + if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) { if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); - a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor, + a.getValue(R.styleable.Window_windowFixedHeightMajor, mFixedHeightMajor); } - if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) { + if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) { if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); - a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor, + a.getValue(R.styleable.Window_windowFixedHeightMinor, mFixedHeightMinor); } + if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) { + requestFeature(FEATURE_CONTENT_TRANSITIONS); + } + if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) { + requestFeature(FEATURE_ACTIVITY_TRANSITIONS); + } final WindowManager windowService = (WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE); if (windowService != null) { final Display display = windowService.getDefaultDisplay(); - if (display.getDisplayId() == Display.DEFAULT_DISPLAY && - a.hasValue(com.android.internal.R.styleable.Window_windowOutsetBottom)) { + final boolean shouldUseBottomOutset = + display.getDisplayId() == Display.DEFAULT_DISPLAY + || (getForcedWindowFlags() & FLAG_FULLSCREEN) != 0; + if (shouldUseBottomOutset && a.hasValue(R.styleable.Window_windowOutsetBottom)) { if (mOutsetBottom == null) mOutsetBottom = new TypedValue(); - a.getValue(com.android.internal.R.styleable.Window_windowOutsetBottom, + a.getValue(R.styleable.Window_windowOutsetBottom, mOutsetBottom); } } @@ -2958,8 +3334,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final int targetSdk = context.getApplicationInfo().targetSdkVersion; final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; + final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.L; final boolean targetHcNeedsOptions = context.getResources().getBoolean( - com.android.internal.R.bool.target_honeycomb_needs_options_menu); + R.bool.target_honeycomb_needs_options_menu); final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { @@ -2967,25 +3344,42 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } else { clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); } - + + // Non-floating windows on high end devices must put up decor beneath the system bars and + // therefore must know about visibility changes of those. + if (!mIsFloating && ActivityManager.isHighEndGfx()) { + if (!targetPreL && a.getBoolean( + R.styleable.Window_windowDrawsSystemBarBackgrounds, + false)) { + setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags()); + } + } + if (!mForcedStatusBarColor) { + mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); + } + if (!mForcedNavigationBarColor) { + mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); + } + if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( - com.android.internal.R.styleable.Window_windowCloseOnTouchOutside, + R.styleable.Window_windowCloseOnTouchOutside, false)) { setCloseOnTouchOutsideIfNotSet(true); } } - + WindowManager.LayoutParams params = getAttributes(); if (!hasSoftInputMode()) { params.softInputMode = a.getInt( - com.android.internal.R.styleable.Window_windowSoftInputMode, + R.styleable.Window_windowSoftInputMode, params.softInputMode); } - if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled, + if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, mIsFloating)) { /* All dialogs should have the window dimmed */ if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { @@ -2999,7 +3393,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( - com.android.internal.R.styleable.Window_windowAnimationStyle, 0); + R.styleable.Window_windowAnimationStyle, 0); } // The rest are only done if this window is not embedded; otherwise, @@ -3008,18 +3402,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mBackgroundDrawable == null) { if (mBackgroundResource == 0) { mBackgroundResource = a.getResourceId( - com.android.internal.R.styleable.Window_windowBackground, 0); + R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { - mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0); + mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); } + mBackgroundFallbackResource = a.getResourceId( + R.styleable.Window_windowBackgroundFallback, 0); if (false) { System.out.println("Background: " + Integer.toHexString(mBackgroundResource) + " Frame: " + Integer.toHexString(mFrameResource)); } } - mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000); + mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); + mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); + mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); } // Inflate the window decor. @@ -3028,15 +3426,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { - layoutResource = com.android.internal.R.layout.screen_swipe_dismiss; + layoutResource = R.layout.screen_swipe_dismiss; } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( - com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true); + R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { - layoutResource = com.android.internal.R.layout.screen_title_icons; + layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); @@ -3045,7 +3443,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. - layoutResource = com.android.internal.R.layout.screen_progress; + layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. @@ -3053,10 +3451,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( - com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); + R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { - layoutResource = com.android.internal.R.layout.screen_custom_title; + layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); @@ -3066,19 +3464,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( - com.android.internal.R.attr.dialogTitleDecorLayout, res, true); + R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { - layoutResource = com.android.internal.R.layout.screen_action_bar; + layoutResource = a.getResourceId( + R.styleable.Window_windowActionBarFullscreenDecorLayout, + R.layout.screen_action_bar); } else { - layoutResource = com.android.internal.R.layout.screen_title; + layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { - layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode; + layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. - layoutResource = com.android.internal.R.layout.screen_simple; + layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } @@ -3086,6 +3486,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { @@ -3106,28 +3507,32 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { - Drawable drawable = mBackgroundDrawable; + final Drawable background; if (mBackgroundResource != 0) { - drawable = getContext().getResources().getDrawable(mBackgroundResource); + background = getContext().getDrawable(mBackgroundResource); + } else { + background = mBackgroundDrawable; } - mDecor.setWindowBackground(drawable); - drawable = null; + mDecor.setWindowBackground(background); + + final Drawable frame; if (mFrameResource != 0) { - drawable = getContext().getResources().getDrawable(mFrameResource); + frame = getContext().getDrawable(mFrameResource); + } else { + frame = null; } - mDecor.setWindowFrame(drawable); - - // System.out.println("Text=" + Integer.toHexString(mTextColor) + - // " Sel=" + Integer.toHexString(mTextSelectedColor) + - // " Title=" + Integer.toHexString(mTitleColor)); + mDecor.setWindowFrame(frame); - if (mTitleColor == 0) { - mTitleColor = mTextColor; - } + mDecor.setElevation(mElevation); + mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); } + + if (mTitleColor == 0) { + mTitleColor = mTextColor; + } setTitleColor(mTitleColor); } @@ -3156,99 +3561,143 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor.makeOptionalFitsSystemWindows(); - mTitleView = (TextView)findViewById(com.android.internal.R.id.title); - if (mTitleView != null) { - mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); - if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { - View titleContainer = findViewById(com.android.internal.R.id.title_container); - if (titleContainer != null) { - titleContainer.setVisibility(View.GONE); - } else { - mTitleView.setVisibility(View.GONE); - } - if (mContentParent instanceof FrameLayout) { - ((FrameLayout)mContentParent).setForeground(null); - } - } else { - mTitleView.setText(mTitle); + final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( + R.id.decor_content_parent); + + if (decorContentParent != null) { + mDecorContentParent = decorContentParent; + mDecorContentParent.setWindowCallback(getCallback()); + if (mDecorContentParent.getTitle() == null) { + mDecorContentParent.setWindowTitle(mTitle); } - } else { - mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); - if (mActionBar != null) { - mActionBar.setWindowCallback(getCallback()); - if (mActionBar.getTitle() == null) { - mActionBar.setWindowTitle(mTitle); - } - final int localFeatures = getLocalFeatures(); - if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) { - mActionBar.initProgress(); - } - if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { - mActionBar.initIndeterminateProgress(); - } - final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById( - com.android.internal.R.id.action_bar_overlay_layout); - if (abol != null) { - abol.setOverlayMode( - (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0); + final int localFeatures = getLocalFeatures(); + for (int i = 0; i < FEATURE_MAX; i++) { + if ((localFeatures & (1 << i)) != 0) { + mDecorContentParent.initFeature(i); } + } + + mDecorContentParent.setUiOptions(mUiOptions); + + if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || + (mIconRes != 0 && !mDecorContentParent.hasIcon())) { + mDecorContentParent.setIcon(mIconRes); + } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && + mIconRes == 0 && !mDecorContentParent.hasIcon()) { + mDecorContentParent.setIcon( + getContext().getPackageManager().getDefaultActivityIcon()); + mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; + } + if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || + (mLogoRes != 0 && !mDecorContentParent.hasLogo())) { + mDecorContentParent.setLogo(mLogoRes); + } - boolean splitActionBar = false; - final boolean splitWhenNarrow = - (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0; - if (splitWhenNarrow) { - splitActionBar = getContext().getResources().getBoolean( - com.android.internal.R.bool.split_action_bar_is_narrow); + // Invalidate if the panel menu hasn't been created before this. + // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu + // being called in the middle of onCreate or similar. + // A pending invalidation will typically be resolved before the posted message + // would run normally in order to satisfy instance state restoration. + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + if (!isDestroyed() && (st == null || st.menu == null)) { + invalidatePanelMenu(FEATURE_ACTION_BAR); + } + } else { + mTitleView = (TextView)findViewById(R.id.title); + if (mTitleView != null) { + mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); + if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { + View titleContainer = findViewById( + R.id.title_container); + if (titleContainer != null) { + titleContainer.setVisibility(View.GONE); + } else { + mTitleView.setVisibility(View.GONE); + } + if (mContentParent instanceof FrameLayout) { + ((FrameLayout)mContentParent).setForeground(null); + } } else { - splitActionBar = getWindowStyle().getBoolean( - com.android.internal.R.styleable.Window_windowSplitActionBar, false); - } - final ActionBarContainer splitView = (ActionBarContainer) findViewById( - com.android.internal.R.id.split_action_bar); - if (splitView != null) { - mActionBar.setSplitView(splitView); - mActionBar.setSplitActionBar(splitActionBar); - mActionBar.setSplitWhenNarrow(splitWhenNarrow); - - final ActionBarContextView cab = (ActionBarContextView) findViewById( - com.android.internal.R.id.action_context_bar); - cab.setSplitView(splitView); - cab.setSplitActionBar(splitActionBar); - cab.setSplitWhenNarrow(splitWhenNarrow); - } else if (splitActionBar) { - Log.e(TAG, "Requested split action bar with " + - "incompatible window decor! Ignoring request."); + mTitleView.setText(mTitle); } + } + } - if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || - (mIconRes != 0 && !mActionBar.hasIcon())) { - mActionBar.setIcon(mIconRes); - } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && - mIconRes == 0 && !mActionBar.hasIcon()) { - mActionBar.setIcon( - getContext().getPackageManager().getDefaultActivityIcon()); - mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; - } - if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || - (mLogoRes != 0 && !mActionBar.hasLogo())) { - mActionBar.setLogo(mLogoRes); + if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { + mDecor.setBackgroundFallback(mBackgroundFallbackResource); + } + + // Only inflate or create a new TransitionManager if the caller hasn't + // already set a custom one. + if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) { + if (mTransitionManager == null) { + final int transitionRes = getWindowStyle().getResourceId( + R.styleable.Window_windowContentTransitionManager, + 0); + if (transitionRes != 0) { + final TransitionInflater inflater = TransitionInflater.from(getContext()); + mTransitionManager = inflater.inflateTransitionManager(transitionRes, + mContentParent); + } else { + mTransitionManager = new TransitionManager(); } + } - // Post the panel invalidate for later; avoid application onCreateOptionsMenu - // being called in the middle of onCreate or similar. - mDecor.post(new Runnable() { - public void run() { - // Invalidate if the panel menu hasn't been created before this. - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); - if (!isDestroyed() && (st == null || st.menu == null)) { - invalidatePanelMenu(FEATURE_ACTION_BAR); - } - } - }); + mEnterTransition = getTransition(mEnterTransition, null, + R.styleable.Window_windowEnterTransition); + mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION, + R.styleable.Window_windowReturnTransition); + mExitTransition = getTransition(mExitTransition, null, + R.styleable.Window_windowExitTransition); + mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION, + R.styleable.Window_windowReenterTransition); + mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null, + R.styleable.Window_windowSharedElementEnterTransition); + mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition, + USE_DEFAULT_TRANSITION, + R.styleable.Window_windowSharedElementReturnTransition); + mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null, + R.styleable.Window_windowSharedElementExitTransition); + mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition, + USE_DEFAULT_TRANSITION, + R.styleable.Window_windowSharedElementReenterTransition); + if (mAllowEnterTransitionOverlap == null) { + mAllowEnterTransitionOverlap = getWindowStyle().getBoolean( + R.styleable.Window_windowAllowEnterTransitionOverlap, true); + } + if (mAllowReturnTransitionOverlap == null) { + mAllowReturnTransitionOverlap = getWindowStyle().getBoolean( + R.styleable.Window_windowAllowReturnTransitionOverlap, true); } + if (mBackgroundFadeDurationMillis < 0) { + mBackgroundFadeDurationMillis = getWindowStyle().getInteger( + R.styleable.Window_windowTransitionBackgroundFadeDuration, + DEFAULT_BACKGROUND_FADE_DURATION_MS); + } + if (mSharedElementsUseOverlay == null) { + mSharedElementsUseOverlay = getWindowStyle().getBoolean( + R.styleable.Window_windowSharedElementsUseOverlay, true); + } + } + } + } + + private Transition getTransition(Transition currentValue, Transition defaultValue, int id) { + if (currentValue != defaultValue) { + return currentValue; + } + int transitionId = getWindowStyle().getResourceId(id, -1); + Transition transition = defaultValue; + if (transitionId != -1 && transitionId != R.transition.no_transition) { + TransitionInflater inflater = TransitionInflater.from(getContext()); + transition = inflater.inflateTransition(transitionId); + if (transition instanceof TransitionSet && + ((TransitionSet)transition).getTransitionCount() == 0) { + transition = null; } } + return transition; } private Drawable loadImageURI(Uri uri) { @@ -3418,7 +3867,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null) { installDecor(); } - return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon)); + return (mLeftIconView = (ImageView)findViewById(R.id.left_icon)); + } + + @Override + protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) { + super.dispatchWindowAttributesChanged(attrs); + if (mDecor != null) { + mDecor.updateColorViews(null /* insets */); + } } private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) { @@ -3428,7 +3885,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null && shouldInstallDecor) { installDecor(); } - mCircularProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_circular); + mCircularProgressBar = (ProgressBar) findViewById(R.id.progress_circular); if (mCircularProgressBar != null) { mCircularProgressBar.setVisibility(View.INVISIBLE); } @@ -3442,7 +3899,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null && shouldInstallDecor) { installDecor(); } - mHorizontalProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_horizontal); + mHorizontalProgressBar = (ProgressBar) findViewById(R.id.progress_horizontal); if (mHorizontalProgressBar != null) { mHorizontalProgressBar.setVisibility(View.INVISIBLE); } @@ -3456,12 +3913,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null) { installDecor(); } - return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon)); + return (mRightIconView = (ImageView)findViewById(R.id.right_icon)); } private void registerSwipeCallbacks() { SwipeDismissLayout swipeDismiss = - (SwipeDismissLayout) findViewById(com.android.internal.R.id.content); + (SwipeDismissLayout) findViewById(R.id.content); swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() { @Override public void onDismissed(SwipeDismissLayout layout) { @@ -3566,6 +4023,150 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mVolumeControlStreamType; } + @Override + public void setMediaController(MediaController controller) { + mMediaController = controller; + } + + @Override + public MediaController getMediaController() { + return mMediaController; + } + + private boolean isTranslucent() { + TypedArray a = getWindowStyle(); + return a.getBoolean(a.getResourceId( + R.styleable.Window_windowIsTranslucent, 0), false); + } + + @Override + public void setEnterTransition(Transition enterTransition) { + mEnterTransition = enterTransition; + } + + @Override + public void setReturnTransition(Transition transition) { + mReturnTransition = transition; + } + + @Override + public void setExitTransition(Transition exitTransition) { + mExitTransition = exitTransition; + } + + @Override + public void setReenterTransition(Transition transition) { + mReenterTransition = transition; + } + + @Override + public void setSharedElementEnterTransition(Transition sharedElementEnterTransition) { + mSharedElementEnterTransition = sharedElementEnterTransition; + } + + @Override + public void setSharedElementReturnTransition(Transition transition) { + mSharedElementReturnTransition = transition; + } + + @Override + public void setSharedElementExitTransition(Transition sharedElementExitTransition) { + mSharedElementExitTransition = sharedElementExitTransition; + } + + @Override + public void setSharedElementReenterTransition(Transition transition) { + mSharedElementReenterTransition = transition; + } + + @Override + public Transition getEnterTransition() { + return mEnterTransition; + } + + @Override + public Transition getReturnTransition() { + return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition() + : mReturnTransition; + } + + @Override + public Transition getExitTransition() { + return mExitTransition; + } + + @Override + public Transition getReenterTransition() { + return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition() + : mReenterTransition; + } + + @Override + public Transition getSharedElementEnterTransition() { + return mSharedElementEnterTransition; + } + + @Override + public Transition getSharedElementReturnTransition() { + return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION + ? getSharedElementEnterTransition() : mSharedElementReturnTransition; + } + + @Override + public Transition getSharedElementExitTransition() { + return mSharedElementExitTransition; + } + + @Override + public Transition getSharedElementReenterTransition() { + return mSharedElementReenterTransition == USE_DEFAULT_TRANSITION + ? getSharedElementExitTransition() : mSharedElementReenterTransition; + } + + @Override + public void setAllowEnterTransitionOverlap(boolean allow) { + mAllowEnterTransitionOverlap = allow; + } + + @Override + public boolean getAllowEnterTransitionOverlap() { + return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap; + } + + @Override + public void setAllowReturnTransitionOverlap(boolean allowExitTransitionOverlap) { + mAllowReturnTransitionOverlap = allowExitTransitionOverlap; + } + + @Override + public boolean getAllowReturnTransitionOverlap() { + return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap; + } + + @Override + public long getTransitionBackgroundFadeDuration() { + return (mBackgroundFadeDurationMillis < 0) ? DEFAULT_BACKGROUND_FADE_DURATION_MS + : mBackgroundFadeDurationMillis; + } + + @Override + public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { + if (fadeDurationMillis < 0) { + throw new IllegalArgumentException("negative durations are not allowed"); + } + mBackgroundFadeDurationMillis = fadeDurationMillis; + } + + @Override + public void setSharedElementsUseOverlay(boolean sharedElementsUseOverlay) { + mSharedElementsUseOverlay = sharedElementsUseOverlay; + } + + @Override + public boolean getSharedElementsUseOverlay() { + return (mSharedElementsUseOverlay == null) ? true : mSharedElementsUseOverlay; + } + private static final class DrawableFeatureState { DrawableFeatureState(int _featureId) { featureId = _featureId; @@ -3657,11 +4258,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean refreshDecorView; boolean refreshMenuContent; - + boolean wasLastOpen; - + boolean wasLastExpanded; - + /** * Contains the state of the menu when told to freeze. */ @@ -3707,18 +4308,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } void setStyle(Context context) { - TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme); + TypedArray a = context.obtainStyledAttributes(R.styleable.Theme); background = a.getResourceId( - com.android.internal.R.styleable.Theme_panelBackground, 0); + R.styleable.Theme_panelBackground, 0); fullBackground = a.getResourceId( - com.android.internal.R.styleable.Theme_panelFullBackground, 0); + R.styleable.Theme_panelFullBackground, 0); windowAnimations = a.getResourceId( - com.android.internal.R.styleable.Theme_windowAnimationStyle, 0); + R.styleable.Theme_windowAnimationStyle, 0); isCompact = a.getBoolean( - com.android.internal.R.styleable.Theme_panelMenuIsCompact, false); + R.styleable.Theme_panelMenuIsCompact, false); listPresenterTheme = a.getResourceId( - com.android.internal.R.styleable.Theme_panelMenuListTheme, - com.android.internal.R.style.Theme_ExpandedMenu); + R.styleable.Theme_panelMenuListTheme, + R.style.Theme_ExpandedMenu); a.recycle(); } @@ -3745,9 +4346,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (listMenuPresenter == null) { listMenuPresenter = new ListMenuPresenter( - com.android.internal.R.layout.list_menu_item_layout, listPresenterTheme); + R.layout.list_menu_item_layout, listPresenterTheme); listMenuPresenter.setCallback(cb); - listMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter); + listMenuPresenter.setId(R.id.list_menu_presenter); menu.addMenuPresenter(listMenuPresenter); } @@ -3766,7 +4367,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (iconMenuPresenter == null) { iconMenuPresenter = new IconMenuPresenter(context); iconMenuPresenter.setCallback(cb); - iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter); + iconMenuPresenter.setId(R.id.icon_menu_presenter); menu.addMenuPresenter(iconMenuPresenter); } @@ -3999,4 +4600,32 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { void sendCloseSystemWindows(String reason) { PhoneWindowManager.sendCloseSystemWindows(getContext(), reason); } + + @Override + public int getStatusBarColor() { + return mStatusBarColor; + } + + @Override + public void setStatusBarColor(int color) { + mStatusBarColor = color; + mForcedStatusBarColor = true; + if (mDecor != null) { + mDecor.updateColorViews(null); + } + } + + @Override + public int getNavigationBarColor() { + return mNavigationBarColor; + } + + @Override + public void setNavigationBarColor(int color) { + mNavigationBarColor = color; + mForcedNavigationBarColor = true; + if (mDecor != null) { + mDecor.updateColorViews(null); + } + } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 44ea4f3..f7ed364 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -41,16 +41,16 @@ import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.media.AudioAttributes; import android.media.AudioManager; -import android.media.AudioSystem; import android.media.IAudioService; import android.media.Ringtone; import android.media.RingtoneManager; +import android.media.session.MediaSessionLegacyHelper; import android.os.Bundle; import android.os.FactoryTest; import android.os.Handler; import android.os.IBinder; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.Messenger; @@ -62,9 +62,13 @@ import android.os.SystemProperties; import android.os.UEventObserver; import android.os.UserHandle; import android.os.Vibrator; +import android.provider.MediaStore; import android.provider.Settings; +import android.service.dreams.DreamManagerInternal; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.speech.RecognizerIntent; +import android.telecom.TelecomManager; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -92,15 +96,18 @@ import android.view.WindowManagerGlobal; import android.view.WindowManagerInternal; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; +import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import com.android.internal.R; +import com.android.internal.policy.IKeyguardService; +import com.android.internal.policy.IKeyguardServiceConstants; import com.android.internal.policy.PolicyManager; import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate; import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate.ShowListener; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.telephony.ITelephony; import com.android.internal.widget.PointerLocationView; import com.android.server.LocalServices; @@ -108,13 +115,15 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashSet; import static android.view.WindowManager.LayoutParams.*; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; +import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; +import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; +import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; /** * WindowManagerPolicy implementation for the Android phone UI. This @@ -177,12 +186,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_TRANSLUCENT - | View.NAVIGATION_BAR_TRANSLUCENT; + | View.NAVIGATION_BAR_TRANSLUCENT + | View.SYSTEM_UI_TRANSPARENT; + + private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build(); /** * Keyguard stuff */ private WindowState mKeyguardScrim; + private boolean mKeyguardHidden; + private boolean mKeyguardDrawnOnce; /* Table of Application Launch keys. Maps from key codes to intent categories. * @@ -207,6 +224,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR); } + /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */ + static final int WAITING_FOR_DRAWN_TIMEOUT = 1000; + /** * Lock protecting internal state. Must not call out into window * manager with lock held. (This lock will be acquired in places @@ -219,11 +239,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManagerFuncs mWindowManagerFuncs; WindowManagerInternal mWindowManagerInternal; PowerManager mPowerManager; + DreamManagerInternal mDreamManagerInternal; IStatusBarService mStatusBarService; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes SearchManager mSearchManager; + AccessibilityManager mAccessibilityManager; // Vibrator pattern for haptic feedback of a long press. long[] mLongPressVibePattern; @@ -234,6 +256,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Vibrator pattern for a short vibration. long[] mKeyboardTapVibePattern; + // Vibrator pattern for a short vibration when tapping on an hour/minute tick of a Clock. + long[] mClockTickVibePattern; + + // Vibrator pattern for a short vibration when tapping on a day/month/year date of a Calendar. + long[] mCalendarDateVibePattern; + // Vibrator pattern for haptic feedback during boot when safe mode is disabled. long[] mSafeModeDisabledVibePattern; @@ -254,15 +282,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { int[] mNavigationBarHeightForRotation = new int[4]; int[] mNavigationBarWidthForRotation = new int[4]; - WindowState mKeyguard = null; + boolean mBootMessageNeedsHiding; KeyguardServiceDelegate mKeyguardDelegate; - // The following are only accessed on the mHandler thread. - boolean mKeyguardDrawComplete; - boolean mWindowManagerDrawComplete; - ArrayList<ScreenOnListener> mScreenOnListeners = new ArrayList<ScreenOnListener>(); - final IRemoteCallback mWindowManagerDrawCallback = new IRemoteCallback.Stub() { + final Runnable mWindowManagerDrawCallback = new Runnable() { @Override - public void sendResult(Bundle data) { + public void run() { if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!"); mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE); } @@ -282,16 +306,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mLastInputMethodWindow = null; WindowState mLastInputMethodTargetWindow = null; - static final int RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS = 0; - static final int RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW = 1; - static final int RECENT_APPS_BEHAVIOR_DISMISS = 2; - static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 3; - - RecentApplicationsDialog mRecentAppsDialog; - int mRecentAppsDialogHeldModifiers; + boolean mRecentsVisible; + int mRecentAppsHeldModifiers; boolean mLanguageSwitchKeyPressed; int mLidState = LID_ABSENT; + int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT; boolean mHaveBuiltInKeyboard; boolean mSystemReady; @@ -307,6 +327,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mDemoHdmiRotation; boolean mDemoHdmiRotationLock; + boolean mWakeGestureEnabledSetting; + MyWakeGestureListener mWakeGestureListener; + // Default display does not rotate, apps that require non-default orientation will have to // have the orientation emulated. private boolean mForceDefaultOrientation = false; @@ -324,12 +347,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mLidControlsSleep; int mShortPressOnPowerBehavior = -1; int mLongPressOnPowerBehavior = -1; - boolean mScreenOnEarly = false; - boolean mScreenOnFully = false; + boolean mAwake; + boolean mScreenOnEarly; + boolean mScreenOnFully; + ScreenOnListener mScreenOnListener; + boolean mKeyguardDrawComplete; + boolean mWindowManagerDrawComplete; boolean mOrientationSensorEnabled = false; int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mHasSoftInput = false; - boolean mTouchExplorationEnabled = false; boolean mTranslucentDecorEnabled = true; int mPointerLocationMode = 0; // guarded by mLock @@ -338,17 +364,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mFocusedWindow; IApplicationToken mFocusedApp; - private final class PointerLocationPointerEventListener implements PointerEventListener { - @Override - public void onPointerEvent(MotionEvent motionEvent) { - if (mPointerLocationView != null) { - mPointerLocationView.addPointerEvent(motionEvent); - } - } - } - - // Pointer location view state, only modified on the mHandler Looper. - PointerLocationPointerEventListener mPointerLocationPointerEventListener; PointerLocationView mPointerLocationView; // The current size of the screen; really; extends into the overscan area of @@ -385,6 +400,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // the same as mCur*, but may be larger if the screen decor has supplied // content insets. int mContentLeft, mContentTop, mContentRight, mContentBottom; + // During layout, the frame in which voice content should be displayed + // to the user, accounting for all screen decoration except for any + // space they deem as available for other content. + int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom; // During layout, the current screen borders along which input method // windows are placed. int mDockLeft, mDockTop, mDockRight, mDockBottom; @@ -411,10 +430,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final Rect mTmpContentFrame = new Rect(); static final Rect mTmpVisibleFrame = new Rect(); static final Rect mTmpDecorFrame = new Rect(); + static final Rect mTmpStableFrame = new Rect(); static final Rect mTmpNavigationFrame = new Rect(); WindowState mTopFullscreenOpaqueWindowState; HashSet<IApplicationToken> mAppsToBeHidden = new HashSet<IApplicationToken>(); + HashSet<IApplicationToken> mAppsThatDismissKeyguard = new HashSet<IApplicationToken>(); boolean mTopIsFullscreen; boolean mForceStatusBar; boolean mForceStatusBarFromKeyguard; @@ -432,6 +453,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { * be done once per window. */ private WindowState mWinDismissingKeyguard; + /** The window that is currently showing "over" the keyguard. If there is an app window + * belonging to another app on top of this the keyguard shows. If there is a fullscreen + * app window under this, still dismiss the keyguard but don't show the app underneath. Show + * the wallpaper. */ + private WindowState mWinShowWhenLocked; + boolean mShowingLockscreen; boolean mShowingDream; boolean mDreamingLockscreen; @@ -444,6 +471,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mSearchKeyShortcutPending; boolean mConsumeSearchKeyUp; boolean mAssistKeyLongPressed; + boolean mPendingMetaAction; // support for activating the lock screen while the screen is on boolean mAllowLockscreenWhenOn; @@ -505,6 +533,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions = new SparseArray<KeyCharacterMap.FallbackAction>(); + private final LogDecelerateInterpolator mLogDecelerateInterpolator + = new LogDecelerateInterpolator(100, 0); + private static final int MSG_ENABLE_POINTER_LOCATION = 1; private static final int MSG_DISABLE_POINTER_LOCATION = 2; private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; @@ -512,7 +543,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; private static final int MSG_KEYGUARD_DRAWN_TIMEOUT = 6; private static final int MSG_WINDOW_MANAGER_DRAWN_COMPLETE = 7; - private static final int MSG_WAKING_UP = 8; + private static final int MSG_DISPATCH_SHOW_RECENTS = 9; + private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10; + private static final int MSG_HIDE_BOOT_MESSAGE = 11; + private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12; private class PolicyHandler extends Handler { @Override @@ -530,24 +564,29 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK: dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj); break; + case MSG_DISPATCH_SHOW_RECENTS: + showRecentApps(false); + break; + case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS: + showGlobalActionsInternal(); + break; case MSG_KEYGUARD_DRAWN_COMPLETE: if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete"); - mKeyguardDrawComplete = true; - finishScreenTurningOn(); + finishKeyguardDrawn(); break; case MSG_KEYGUARD_DRAWN_TIMEOUT: Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete"); - mKeyguardDrawComplete = true; - finishScreenTurningOn(); + finishKeyguardDrawn(); break; case MSG_WINDOW_MANAGER_DRAWN_COMPLETE: if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete"); - mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); - mWindowManagerDrawComplete = true; - finishScreenTurningOn(); + finishWindowsDrawn(); + break; + case MSG_HIDE_BOOT_MESSAGE: + handleHideBootMessage(); break; - case MSG_WAKING_UP: - handleWakingUp((ScreenOnListener) msg.obj); + case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: + launchVoiceAssistWithWakeLock(msg.arg1 != 0); break; } } @@ -574,6 +613,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.WAKE_GESTURE_ENABLED), false, this, + UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.ACCELEROMETER_ROTATION), false, this, UserHandle.USER_ALL); @@ -589,9 +631,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(Settings.System.getUriFor( + resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.POLICY_CONTROL), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -601,6 +646,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + class MyWakeGestureListener extends WakeGestureListener { + MyWakeGestureListener(Context context, Handler handler) { + super(context, handler); + } + + @Override + public void onWakeUp() { + synchronized (mLock) { + if (shouldEnableWakeGestureLp()) { + performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); + } + } + } + } + class MyOrientationListener extends WindowOrientationListener { MyOrientationListener(Context context, Handler handler) { super(context, handler); @@ -697,11 +758,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } //Could have been invoked due to screen turning on or off or //change of the currently visible window's orientation - if (localLOGV) Slog.v(TAG, "Screen status="+mScreenOnEarly+ - ", current orientation="+mCurrentAppOrientation+ - ", SensorEnabled="+mOrientationSensorEnabled); + if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly + + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation + + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled); boolean disable = true; - if (mScreenOnEarly) { + if (mScreenOnEarly && mAwake) { if (needSensorRunningLp()) { disable = false; //enable listener if not already enabled @@ -783,16 +844,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case SHORT_PRESS_POWER_GO_TO_SLEEP: mPowerManager.goToSleep(eventTime, - PowerManager.GO_TO_SLEEP_REASON_USER, 0); + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: mPowerManager.goToSleep(eventTime, - PowerManager.GO_TO_SLEEP_REASON_USER, + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: mPowerManager.goToSleep(eventTime, - PowerManager.GO_TO_SLEEP_REASON_USER, + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); launchHomeFromHotKey(); break; @@ -820,8 +881,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) { performAuditoryFeedbackForAccessibilityIfNeed(); } - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - showGlobalActionsDialog(); + showGlobalActionsInternal(); break; case LONG_PRESS_POWER_SHUT_OFF: case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: @@ -841,7 +901,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - void showGlobalActionsDialog() { + @Override + public void showGlobalActions() { + mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS); + mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS); + } + + void showGlobalActionsInternal() { + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); if (mGlobalActions == null) { mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); } @@ -859,6 +926,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; } + boolean isUserSetupComplete() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + } + private void handleLongPressOnHome() { if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) { mHomeConsumed = true; @@ -889,52 +961,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - /** - * Create (if necessary) and show or dismiss the recent apps dialog according - * according to the requested behavior. - */ - void showOrHideRecentAppsDialog(final int behavior) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mRecentAppsDialog == null) { - mRecentAppsDialog = new RecentApplicationsDialog(mContext); - } - if (mRecentAppsDialog.isShowing()) { - switch (behavior) { - case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS: - case RECENT_APPS_BEHAVIOR_DISMISS: - mRecentAppsDialog.dismiss(); - break; - case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH: - mRecentAppsDialog.dismissAndSwitch(); - break; - case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW: - default: - break; - } - } else { - switch (behavior) { - case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS: - mRecentAppsDialog.show(); - break; - case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW: - try { - mWindowManager.setInTouchMode(false); - } catch (RemoteException e) { - } - mRecentAppsDialog.show(); - break; - case RECENT_APPS_BEHAVIOR_DISMISS: - case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH: - default: - break; - } - } - } - }); - } - /** {@inheritDoc} */ @Override public void init(Context context, IWindowManager windowManager, @@ -943,8 +969,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); mHandler = new PolicyHandler(); + mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); mOrientationListener = new MyOrientationListener(mContext, mHandler); try { mOrientationListener.setCurrentRotation(windowManager.getRotation()); @@ -996,6 +1024,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.bool.config_enableTranslucentDecor); readConfigurationDependentBehaviors(); + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + // register for dock events IntentFilter filter = new IntentFilter(); filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE); @@ -1056,6 +1087,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.array.config_virtualKeyVibePattern); mKeyboardTapVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_keyboardTapVibePattern); + mClockTickVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_clockTickVibePattern); + mCalendarDateVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_calendarDateVibePattern); mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_safeModeDisabledVibePattern); mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), @@ -1070,9 +1105,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { initializeHdmiState(); // Match current screen state. - if (mPowerManager.isInteractive()) { - wakingUp(null); - } else { + if (!mPowerManager.isInteractive()) { goingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER); } } @@ -1194,7 +1227,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * navigation bar and touch exploration is not enabled */ private boolean canHideNavigationBar() { - return mHasNavigationBar && !mTouchExplorationEnabled; + return mHasNavigationBar + && !mAccessibilityManager.isTouchExplorationEnabled(); } @Override @@ -1225,6 +1259,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT, UserHandle.USER_CURRENT); + // Configure wake gesture. + boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver, + Settings.Secure.WAKE_GESTURE_ENABLED, 0, + UserHandle.USER_CURRENT) != 0; + if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) { + mWakeGestureEnabledSetting = wakeGestureEnabledSetting; + updateWakeGestureListenerLp(); + } + // Configure rotation lock. int userRotation = Settings.System.getIntForUser(resolver, Settings.System.USER_ROTATION, Surface.ROTATION_0, @@ -1263,19 +1306,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { updateRotation = true; } if (mImmersiveModeConfirmation != null) { - mImmersiveModeConfirmation.loadSetting(); + mImmersiveModeConfirmation.loadSetting(mCurrentUserId); } + PolicyControl.reloadFromSetting(mContext); } if (updateRotation) { updateRotation(true); } } + private void updateWakeGestureListenerLp() { + if (shouldEnableWakeGestureLp()) { + mWakeGestureListener.requestWakeUpTrigger(); + } else { + mWakeGestureListener.cancelWakeUpTrigger(); + } + } + + private boolean shouldEnableWakeGestureLp() { + return mWakeGestureEnabledSetting && !mAwake + && (!mLidControlsSleep || mLidState != LID_CLOSED) + && mWakeGestureListener.isSupported(); + } + private void enablePointerLocation() { if (mPointerLocationView == null) { mPointerLocationView = new PointerLocationView(mContext); mPointerLocationView.setPrintCoords(false); - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); @@ -1295,22 +1352,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.getSystemService(Context.WINDOW_SERVICE); lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; wm.addView(mPointerLocationView, lp); - - mPointerLocationPointerEventListener = new PointerLocationPointerEventListener(); - mWindowManagerFuncs.registerPointerEventListener(mPointerLocationPointerEventListener); + mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView); } } private void disablePointerLocation() { - if (mPointerLocationPointerEventListener != null) { - mWindowManagerFuncs.unregisterPointerEventListener( - mPointerLocationPointerEventListener); - mPointerLocationPointerEventListener = null; - } - if (mPointerLocationView != null) { - WindowManager wm = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); + mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView); + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); wm.removeView(mPointerLocationView); mPointerLocationView = null; } @@ -1352,11 +1401,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { // XXX right now the app process has complete control over // this... should introduce a token to let the system // monitor/control what they are doing. + outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW; break; case TYPE_DREAM: case TYPE_INPUT_METHOD: case TYPE_WALLPAPER: case TYPE_PRIVATE_PRESENTATION: + case TYPE_VOICE_INTERACTION: // The window manager will check these. break; case TYPE_PHONE: @@ -1402,7 +1453,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_BOOT_PROGRESS: case TYPE_DISPLAY_OVERLAY: case TYPE_HIDDEN_NAV_CONSUMER: - case TYPE_KEYGUARD: case TYPE_KEYGUARD_SCRIM: case TYPE_KEYGUARD_DIALOG: case TYPE_MAGNIFICATION_OVERLAY: @@ -1439,13 +1489,39 @@ public class PhoneWindowManager implements WindowManagerPolicy { | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; break; + case TYPE_STATUS_BAR: + + // If the Keyguard is in a hidden state (occluded by another window), we force to + // remove the wallpaper and keyguard flag so that any change in-flight after setting + // the keyguard as occluded wouldn't set these flags again. + // See {@link #processKeyguardSetHiddenResultLw}. + if (mKeyguardHidden) { + attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } + break; + } + + if (attrs.type != TYPE_STATUS_BAR) { + // The status bar is the only window allowed to exhibit keyguard behavior. + attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } + + if (ActivityManager.isHighEndGfx() + && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; } } - + void readLidState() { mLidState = mWindowManagerFuncs.getLidState(); } - + + private void readCameraLensCoverState() { + mCameraLensCoverState = mWindowManagerFuncs.getCameraLensCoverState(); + } + private boolean isHidden(int accessibilityMode) { switch (accessibilityMode) { case 1: @@ -1501,41 +1577,40 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 3; case TYPE_SEARCH_BAR: return 4; + case TYPE_VOICE_INTERACTION: + // voice interaction layer is almost immediately above apps. + return 5; case TYPE_RECENTS_OVERLAY: case TYPE_SYSTEM_DIALOG: - return 5; + return 6; case TYPE_TOAST: // toasts and the plugged-in battery thing - return 6; + return 7; case TYPE_PRIORITY_PHONE: // SIM errors and unlock. Not sure if this really should be in a high layer. - return 7; + return 8; case TYPE_DREAM: // used for Dreams (screensavers with TYPE_DREAM windows) - return 8; + return 9; case TYPE_SYSTEM_ALERT: // like the ANR / app crashed dialogs - return 9; + return 10; case TYPE_INPUT_METHOD: // on-screen keyboards and other such input method user interfaces go here. - return 10; + return 11; case TYPE_INPUT_METHOD_DIALOG: // on-screen keyboards and other such input method user interfaces go here. - return 11; + return 12; case TYPE_KEYGUARD_SCRIM: // the safety window that shows behind keyguard while keyguard is starting - return 12; - case TYPE_KEYGUARD: - // the keyguard; nothing on top of these can take focus, since they are - // responsible for power management when displayed. return 13; - case TYPE_KEYGUARD_DIALOG: - return 14; case TYPE_STATUS_BAR_SUB_PANEL: - return 15; + return 14; case TYPE_STATUS_BAR: - return 16; + return 15; case TYPE_STATUS_BAR_PANEL: + return 16; + case TYPE_KEYGUARD_DIALOG: return 17; case TYPE_VOLUME_OVERLAY: // the on-screen volume indicator and controller shown when the user @@ -1606,6 +1681,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return windowTypeToLayerLw(TYPE_SYSTEM_ERROR); } + @Override public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) { if (mHasNavigationBar) { // For a basic navigation bar, when we are in landscape mode we place @@ -1617,6 +1693,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return fullWidth; } + @Override public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation) { if (mHasNavigationBar) { // For a basic navigation bar, when we are in portrait mode we place @@ -1628,10 +1705,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { return fullHeight; } + @Override public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation) { return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation); } + @Override public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation) { // There is a separate status bar at the top of the display. We don't count that as part // of the fixed decor, since it can hide; however, for purposes of configurations, @@ -1641,8 +1720,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) { - return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD; + public boolean isForceHiding(WindowManager.LayoutParams attrs) { + return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || + (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden()) || + (attrs.type == TYPE_KEYGUARD_SCRIM); + } + + @Override + public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { + return attrs.type == TYPE_STATUS_BAR; } @Override @@ -1653,7 +1739,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_WALLPAPER: case TYPE_DREAM: case TYPE_UNIVERSE_BACKGROUND: - case TYPE_KEYGUARD: case TYPE_KEYGUARD_SCRIM: return false; default: @@ -1661,6 +1746,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + @Override + public WindowState getWinShowWhenLockedLw() { + return mWinShowWhenLocked; + } + /** {@inheritDoc} */ @Override public View addStartingWindow(IBinder appToken, String packageName, int theme, @@ -1782,6 +1872,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void removeStartingWindow(IBinder appToken, View window) { if (DEBUG_STARTING_WINDOW) { RuntimeException e = new RuntimeException("here"); @@ -1797,19 +1888,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** * Preflight adding a window to the system. - * + * * Currently enforces that three window types are singletons: * <ul> * <li>STATUS_BAR_TYPE</li> * <li>KEYGUARD_TYPE</li> * </ul> - * + * * @param win The window to be added * @param attrs Information about the window to be added - * + * * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ + @Override public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_STATUS_BAR: @@ -1823,6 +1915,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mStatusBar = win; mStatusBarController.setWindow(win); + mKeyguardDelegate.hideScrim(); break; case TYPE_NAVIGATION_BAR: mContext.enforceCallingOrSelfPermission( @@ -1852,12 +1945,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { android.Manifest.permission.STATUS_BAR_SERVICE, "PhoneWindowManager"); break; - case TYPE_KEYGUARD: - if (mKeyguard != null) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } - mKeyguard = win; - break; case TYPE_KEYGUARD_SCRIM: if (mKeyguardScrim != null) { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; @@ -1870,13 +1957,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void removeWindowLw(WindowState win) { if (mStatusBar == win) { mStatusBar = null; mStatusBarController.setWindow(null); - } else if (mKeyguard == win) { - Log.v(TAG, "Removing keyguard window (Did it crash?)"); - mKeyguard = null; mKeyguardDelegate.showScrim(); } else if (mKeyguardScrim == win) { Log.v(TAG, "Removing keyguard scrim"); @@ -1888,19 +1973,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } static final boolean PRINT_ANIM = false; - + /** {@inheritDoc} */ @Override public int selectAnimationLw(WindowState win, int transit) { if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win + ": transit=" + transit); if (win == mStatusBar) { + boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { - return R.anim.dock_top_exit; + return isKeyguard ? -1 : R.anim.dock_top_exit; } else if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { - return R.anim.dock_top_enter; + return isKeyguard ? -1 : R.anim.dock_top_enter; } } else if (win == mNavigationBar) { // This can be on either the bottom or the right. @@ -1985,10 +2071,38 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public Animation createForceHideEnterAnimation(boolean onWallpaper) { - return AnimationUtils.loadAnimation(mContext, onWallpaper - ? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter - : com.android.internal.R.anim.lock_screen_behind_enter); + public Animation createForceHideEnterAnimation(boolean onWallpaper, + boolean goingToNotificationShade) { + if (goingToNotificationShade) { + return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in); + } else if (onWallpaper) { + Animation a = AnimationUtils.loadAnimation(mContext, + R.anim.lock_screen_behind_enter_wallpaper); + AnimationSet set = (AnimationSet) a; + + // TODO: Use XML interpolators when we have log interpolators available in XML. + set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator); + set.getAnimations().get(1).setInterpolator(mLogDecelerateInterpolator); + return set; + } else { + Animation a = AnimationUtils.loadAnimation(mContext, + R.anim.lock_screen_behind_enter); + AnimationSet set = (AnimationSet) a; + + // TODO: Use XML interpolators when we have log interpolators available in XML. + set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator); + return set; + } + } + + + @Override + public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade) { + if (goingToNotificationShade) { + return null; + } else { + return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit); + } } private static void awakenDreams() { @@ -2007,9 +2121,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { ServiceManager.checkService(DreamService.DREAM_SERVICE)); } - static ITelephony getTelephonyService() { - return ITelephony.Stub.asInterface( - ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + TelecomManager getTelecommService() { + return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); } static IAudioService getAudioService() { @@ -2067,6 +2180,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Cancel any pending meta actions if we see any other keys being pressed between the down + // of the meta key and its corresponding up. + if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) { + mPendingMetaAction = false; + } + // First we always handle the home key here, so applications // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second @@ -2090,16 +2209,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // If an incoming call is ringing, HOME is totally disabled. - // (The user is already on the InCallScreen at this point, + // (The user is already on the InCallUI at this point, // and his ONLY options are to answer or reject the call.) - try { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null && telephonyService.isRinging()) { - Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); - return -1; - } - } catch (RemoteException ex) { - Log.w(TAG, "RemoteException from getPhoneInterface()", ex); + TelecomManager telecomManager = getTelecommService(); + if (telecomManager != null && telecomManager.isRinging()) { + Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); + return -1; } // Delay handling home if a double-tap is possible. @@ -2111,6 +2226,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } + // If there's a dream running then use home to escape the dream + // but don't actually go home. + if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) { + mDreamManagerInternal.stopDream(false /*immediate*/); + return -1; + } + // Go home! launchHomeFromHotKey(); return -1; @@ -2121,9 +2243,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null; if (attrs != null) { final int type = attrs.type; - if (type == WindowManager.LayoutParams.TYPE_KEYGUARD - || type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM - || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { + if (type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM + || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG + || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { // the "app" is keyguard, so give it the key return 0; } @@ -2223,6 +2345,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } return -1; + } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) { + if (!down) { + Intent voiceIntent; + if (!keyguardOn) { + voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + } else { + voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); + voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true); + } + mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF); + } } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) { if (down && repeatCount == 0) { mHandler.post(mScreenshotRunnable); @@ -2261,8 +2394,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT_OR_SELF); - Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF); + mContext.startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), + UserHandle.CURRENT_OR_SELF); + } + return -1; + } else if (KeyEvent.isMetaKey(keyCode)) { + if (down) { + mPendingMetaAction = true; + } else if (mPendingMetaAction) { + launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD); } return -1; } @@ -2336,23 +2476,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - // Display task switcher for ALT-TAB or Meta-TAB. + // Display task switcher for ALT-TAB. if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { - if (mRecentAppsDialogHeldModifiers == 0 && !keyguardOn) { + if (mRecentAppsHeldModifiers == 0 && !keyguardOn) { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; - if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON) - || KeyEvent.metaStateHasModifiers( - shiftlessModifiers, KeyEvent.META_META_ON)) { - mRecentAppsDialogHeldModifiers = shiftlessModifiers; - showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW); + if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { + mRecentAppsHeldModifiers = shiftlessModifiers; + showRecentApps(true); return -1; } } - } else if (!down && mRecentAppsDialogHeldModifiers != 0 - && (metaState & mRecentAppsDialogHeldModifiers) == 0) { - mRecentAppsDialogHeldModifiers = 0; - showOrHideRecentAppsDialog(keyguardOn ? RECENT_APPS_BEHAVIOR_DISMISS : - RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH); + } else if (!down && mRecentAppsHeldModifiers != 0 + && (metaState & mRecentAppsHeldModifiers) == 0) { + mRecentAppsHeldModifiers = 0; + hideRecentApps(true, false); } // Handle keyboard language switching. @@ -2375,6 +2512,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } + // Reserve all the META modifier combos for system behavior + if ((metaState & KeyEvent.META_META_ON) != 0) { + return -1; + } + // Let the application handle the key. return 0; } @@ -2467,7 +2609,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { - // TODO: This only stops the factory-installed search manager. + // TODO: This only stops the factory-installed search manager. // Need to formalize an API to handle others SearchManager searchManager = getSearchManager(); if (searchManager != null) { @@ -2480,10 +2622,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void launchAssistAction() { + launchAssistAction(null); + } + + private void launchAssistAction(String hint) { sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) .getAssistIntent(mContext, true, UserHandle.USER_CURRENT); if (intent != null) { + if (hint != null) { + intent.putExtra(hint, true); + } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -2525,7 +2674,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { statusbar.cancelPreloadRecentApps(); } } catch (RemoteException e) { - Slog.e(TAG, "RemoteException when showing recent apps", e); + Slog.e(TAG, "RemoteException when cancelling recent apps preload", e); // re-acquire status bar service next time it is needed. mStatusBarService = null; } @@ -2534,30 +2683,64 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void toggleRecentApps() { mPreloadedRecentApps = false; // preloading no longer needs to be canceled - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); try { IStatusBarService statusbar = getStatusBarService(); if (statusbar != null) { statusbar.toggleRecentApps(); } } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when toggling recent apps", e); + // re-acquire status bar service next time it is needed. + mStatusBarService = null; + } + } + + @Override + public void showRecentApps() { + mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS); + mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_RECENTS); + } + + private void showRecentApps(boolean triggeredFromAltTab) { + mPreloadedRecentApps = false; // preloading no longer needs to be canceled + try { + IStatusBarService statusbar = getStatusBarService(); + if (statusbar != null) { + statusbar.showRecentApps(triggeredFromAltTab); + } + } catch (RemoteException e) { Slog.e(TAG, "RemoteException when showing recent apps", e); // re-acquire status bar service next time it is needed. mStatusBarService = null; } } + private void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHome) { + mPreloadedRecentApps = false; // preloading no longer needs to be canceled + try { + IStatusBarService statusbar = getStatusBarService(); + if (statusbar != null) { + statusbar.hideRecentApps(triggeredFromAltTab, triggeredFromHome); + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when closing recent apps", e); + // re-acquire status bar service next time it is needed. + mStatusBarService = null; + } + } + /** * A home key -> launch home action was detected. Take the appropriate action * given the situation with the keyguard. */ void launchHomeFromHotKey() { - if (mKeyguardDelegate != null && mKeyguardDelegate.isShowingAndNotHidden()) { + if (mKeyguardDelegate != null && mKeyguardDelegate.isShowingAndNotOccluded()) { // don't launch home if keyguard showing } else if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) { // when in keyguard restricted mode, must first verify unlock // before launching home mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() { + @Override public void onKeyguardExitResult(boolean success) { if (success) { try { @@ -2575,8 +2758,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityManagerNative.getDefault().stopAppSwitches(); } catch (RemoteException e) { } - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); - startDockOrHome(); + if (mRecentsVisible) { + // Hide Recents and notify it to launch Home + awakenDreams(); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); + hideRecentApps(false, true); + } else { + // Otherwise, just launch Home + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); + startDockOrHome(); + } } } @@ -2660,6 +2851,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); + mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0; // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. @@ -2672,8 +2864,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) { - final int fl = attrs.flags; - final int systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); + final int fl = PolicyControl.getWindowFlags(null, attrs); + final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs); + final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility); if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { @@ -2764,13 +2957,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRestrictedScreenTop = mUnrestrictedScreenTop; mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth; mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight; - mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft + mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft = mCurLeft = mUnrestrictedScreenLeft; - mDockTop = mContentTop = mStableTop = mStableFullscreenTop + mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop = mCurTop = mUnrestrictedScreenTop; - mDockRight = mContentRight = mStableRight = mStableFullscreenRight + mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight = mCurRight = displayWidth - overscanRight; - mDockBottom = mContentBottom = mStableBottom = mStableFullscreenBottom + mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom = mCurBottom = displayHeight - overscanBottom; mDockLayer = 0x10000000; mStatusBarLayer = -1; @@ -2792,12 +2985,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { // drive nav being hidden only by whether it is requested. final int sysui = mLastSystemUiFlags; boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; - boolean navTranslucent = (sysui & View.NAVIGATION_BAR_TRANSLUCENT) != 0; + boolean navTranslucent = (sysui + & (View.NAVIGATION_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean navAllowedHidden = immersive || immersiveSticky; navTranslucent &= !immersiveSticky; // transient trumps translucent - navTranslucent &= areTranslucentBarsAllowed(); + boolean isKeyguardShowing = isStatusBarKeyguard() && !mHideLockScreen; + if (!isKeyguardShowing) { + navTranslucent &= areTranslucentBarsAllowed(); + } // When the navigation bar isn't visible, we put up a fake // input window to catch all touch events. This way we can @@ -2845,7 +3042,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // We currently want to hide the navigation UI. mNavigationBarController.setBarShowingLw(false); } - if (navVisible && !navTranslucent && !mNavigationBar.isAnimatingLw() + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the opaque nav bar is currently requested to be visible, // and not in the process of animating on or off, then @@ -2879,14 +3077,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Make sure the content and current rectangles are updated to // account for the restrictions from the navigation bar. - mContentTop = mCurTop = mDockTop; - mContentBottom = mCurBottom = mDockBottom; - mContentLeft = mCurLeft = mDockLeft; - mContentRight = mCurRight = mDockRight; + mContentTop = mVoiceContentTop = mCurTop = mDockTop; + mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom; + mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft; + mContentRight = mVoiceContentRight = mCurRight = mDockRight; mStatusBarLayer = mNavigationBar.getSurfaceLayer(); // And compute the final frame. mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, - mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf); + mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf, + mTmpNavigationFrame); if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame); if (mNavigationBarController.checkHiddenLw()) { updateSysUiVisibility = true; @@ -2911,14 +3110,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { mStatusBarLayer = mStatusBar.getSurfaceLayer(); // Let the status bar determine its size. - mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf); + mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf, vf); // For layout, the status bar is always at the top with our fixed height. mStableTop = mUnrestrictedScreenTop + mStatusBarHeight; boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; - boolean statusBarTranslucent = (sysui & View.STATUS_BAR_TRANSLUCENT) != 0; - statusBarTranslucent &= areTranslucentBarsAllowed(); + boolean statusBarTranslucent = (sysui + & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0; + if (!isKeyguardShowing) { + statusBarTranslucent &= areTranslucentBarsAllowed(); + } // If the status bar is hidden, we don't want to cause // windows behind it to scroll. @@ -2928,10 +3130,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // status bar is visible. mDockTop = mUnrestrictedScreenTop + mStatusBarHeight; - mContentTop = mCurTop = mDockTop; - mContentBottom = mCurBottom = mDockBottom; - mContentLeft = mCurLeft = mDockLeft; - mContentRight = mCurRight = mDockRight; + mContentTop = mVoiceContentTop = mCurTop = mDockTop; + mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom; + mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft; + mContentRight = mVoiceContentRight = mCurRight = mDockRight; if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( @@ -3002,7 +3204,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Ungh. So to deal with that, make sure the content frame // we end up using is not covering the IM dock. cf.set(attached.getContentFrameLw()); - if (attached.getSurfaceLayer() < mDockLayer) { + if (attached.isVoiceInteraction()) { + if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft; + if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop; + if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight; + if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom; + } else if (attached.getSurfaceLayer() < mDockLayer) { if (cf.left < mContentLeft) cf.left = mContentLeft; if (cf.top < mContentTop) cf.top = mContentTop; if (cf.right > mContentRight) cf.right = mContentRight; @@ -3040,10 +3247,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, - WindowState attached) { + public void layoutWindowLw(WindowState win, WindowState attached) { // we've already done the status bar - if (win == mStatusBar || win == mNavigationBar) { + final WindowManager.LayoutParams attrs = win.getAttrs(); + if ((win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) == 0) || + win == mNavigationBar) { return; } final boolean isDefaultDisplay = win.isDefaultDisplay(); @@ -3054,9 +3262,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { offsetInputMethodWindowLw(mLastInputMethodWindow); } - final int fl = attrs.flags; + final int fl = PolicyControl.getWindowFlags(win, attrs); final int sim = attrs.softInputMode; - final int sysUiFl = win.getSystemUiVisibility(); + final int sysUiFl = PolicyControl.getSystemUiVisibility(win, null); final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; @@ -3064,6 +3272,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Rect cf = mTmpContentFrame; final Rect vf = mTmpVisibleFrame; final Rect dcf = mTmpDecorFrame; + final Rect sf = mTmpStableFrame; dcf.setEmpty(); final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar @@ -3071,6 +3280,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int adjust = sim & SOFT_INPUT_MASK_ADJUST; + if (isDefaultDisplay) { + sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom); + } else { + sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom); + } + if (!isDefaultDisplay) { if (attached != null) { // If this window is attached to another, our display @@ -3096,6 +3311,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { // IM dock windows always go to the bottom of the screen. attrs.gravity = Gravity.BOTTOM; mDockLayer = win.getSurfaceLayer(); + } else if (win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + pf.left = df.left = of.left = mUnrestrictedScreenLeft; + pf.top = df.top = of.top = mUnrestrictedScreenTop; + pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft; + pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop; + cf.left = vf.left = mStableLeft; + cf.top = vf.top = mStableTop; + cf.right = vf.right = mStableRight; + vf.bottom = mStableBottom; + cf.bottom = mContentBottom; } else { // Default policy decor for the default display @@ -3113,12 +3338,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0 - && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0) { + && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0 + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes status bar dcf.top = mStableTop; } if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0 - && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { + && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes navigation bar dcf.bottom = mStableBottom; dcf.right = mStableRight; @@ -3127,7 +3356,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR"); // This is the case for a normal activity window: we want it // to cover all of the screen space, and it can take care of @@ -3172,9 +3401,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { + mOverscanScreenHeight; } else if (canHideNavigationBar() && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD || ( - attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW - && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) { + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { // Asking for layout as if the nav bar is hidden, lets the // application extend into the unrestricted overscan screen area. We // only do this for application windows to ensure no window that @@ -3207,16 +3435,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if ((fl & FLAG_FULLSCREEN) == 0) { - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.left = mDockLeft; - cf.top = mDockTop; - cf.right = mDockRight; - cf.bottom = mDockBottom; + if (win.isVoiceInteraction()) { + cf.left = mVoiceContentLeft; + cf.top = mVoiceContentTop; + cf.right = mVoiceContentRight; + cf.bottom = mVoiceContentBottom; } else { - cf.left = mContentLeft; - cf.top = mContentTop; - cf.right = mContentRight; - cf.bottom = mContentBottom; + if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + cf.left = mDockLeft; + cf.top = mDockTop; + cf.right = mDockRight; + cf.bottom = mDockBottom; + } else { + cf.left = mContentLeft; + cf.top = mContentTop; + cf.right = mContentRight; + cf.bottom = mContentBottom; + } } } else { // Full screen windows are always given a layout that is as if the @@ -3291,13 +3526,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop + mOverscanScreenHeight; } else if (attrs.type == TYPE_WALLPAPER) { - // The wallpaper also has Real Ultimate Power. - pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft; - pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop; - pf.right = df.right = of.right = cf.right - = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; - pf.bottom = df.bottom = of.bottom = cf.bottom - = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; + // The wallpaper also has Real Ultimate Power, but we want to tell + // it about the overscan area. + pf.left = df.left = mOverscanScreenLeft; + pf.top = df.top = mOverscanScreenTop; + pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth; + pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight; + of.left = cf.left = mUnrestrictedScreenLeft; + of.top = cf.top = mUnrestrictedScreenTop; + of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { @@ -3311,7 +3549,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { = mOverscanScreenTop + mOverscanScreenHeight; } else if (canHideNavigationBar() && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (attrs.type == TYPE_TOAST + && (attrs.type == TYPE_STATUS_BAR + || attrs.type == TYPE_TOAST || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) { // Asking for layout as if the nav bar is hidden, lets the @@ -3380,7 +3619,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.top = mContentTop; pf.right = mContentRight; pf.bottom = mContentBottom; - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + if (win.isVoiceInteraction()) { + df.left = of.left = cf.left = mVoiceContentLeft; + df.top = of.top = cf.top = mVoiceContentTop; + df.right = of.right = cf.right = mVoiceContentRight; + df.bottom = of.bottom = cf.bottom = mVoiceContentBottom; + } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { df.left = of.left = cf.left = mDockLeft; df.top = of.top = cf.top = mDockTop; df.right = of.right = cf.right = mDockRight; @@ -3405,21 +3649,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR) { - df.left = df.top = of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; - df.right = df.bottom = of.right = of.bottom = cf.right = cf.bottom - = vf.right = vf.bottom = 10000; + df.left = df.top = -10000; + df.right = df.bottom = 10000; + if (attrs.type != TYPE_WALLPAPER) { + of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; + of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; + } } if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() + ": sim=#" + Integer.toHexString(sim) - + " attach=" + attached + " type=" + attrs.type + + " attach=" + attached + " type=" + attrs.type + String.format(" flags=0x%08x", fl) + " pf=" + pf.toShortString() + " df=" + df.toShortString() + " of=" + of.toShortString() + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() - + " dcf=" + dcf.toShortString()); + + " dcf=" + dcf.toShortString() + + " sf=" + sf.toShortString()); - win.computeFrameLw(pf, df, of, cf, vf, dcf); + win.computeFrameLw(pf, df, of, cf, vf, dcf, sf); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. @@ -3428,6 +3676,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { setLastInputMethodWindowLw(null, null); offsetInputMethodWindowLw(win); } + if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleOrBehindKeyguardLw() + && !win.getGivenInsetsPendingLw()) { + offsetVoiceInputWindowLw(win); + } } private void offsetInputMethodWindowLw(WindowState win) { @@ -3436,6 +3688,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mContentBottom > top) { mContentBottom = top; } + if (mVoiceContentBottom > top) { + mVoiceContentBottom = top; + } top = win.getVisibleFrameLw().top; top += win.getGivenVisibleInsetsLw().top; if (mCurBottom > top) { @@ -3446,6 +3701,40 @@ public class PhoneWindowManager implements WindowManagerPolicy { + mContentBottom + " mCurBottom=" + mCurBottom); } + private void offsetVoiceInputWindowLw(WindowState win) { + final int gravity = win.getAttrs().gravity; + switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER) + << Gravity.AXIS_X_SHIFT)) { + case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_X_SHIFT: { + int right = win.getContentFrameLw().right - win.getGivenContentInsetsLw().right; + if (mVoiceContentLeft < right) { + mVoiceContentLeft = right; + } + } break; + case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_X_SHIFT: { + int left = win.getContentFrameLw().left - win.getGivenContentInsetsLw().left; + if (mVoiceContentRight < left) { + mVoiceContentRight = left; + } + } break; + } + switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER) + << Gravity.AXIS_Y_SHIFT)) { + case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_Y_SHIFT: { + int bottom = win.getContentFrameLw().bottom - win.getGivenContentInsetsLw().bottom; + if (mVoiceContentTop < bottom) { + mVoiceContentTop = bottom; + } + } break; + case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_Y_SHIFT: { + int top = win.getContentFrameLw().top - win.getGivenContentInsetsLw().top; + if (mVoiceContentBottom < top) { + mVoiceContentBottom = top; + } + } break; + } + } + /** {@inheritDoc} */ @Override public void finishLayoutLw() { @@ -3457,43 +3746,48 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { mTopFullscreenOpaqueWindowState = null; mAppsToBeHidden.clear(); + mAppsThatDismissKeyguard.clear(); mForceStatusBar = false; mForceStatusBarFromKeyguard = false; mForcingShowNavBar = false; mForcingShowNavBarLayer = -1; - + mHideLockScreen = false; mAllowLockscreenWhenOn = false; mDismissKeyguard = DISMISS_KEYGUARD_NONE; mShowingLockscreen = false; mShowingDream = false; + mWinShowWhenLocked = null; } /** {@inheritDoc} */ @Override - public void applyPostLayoutPolicyLw(WindowState win, - WindowManager.LayoutParams attrs) { + public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" + win.isVisibleOrBehindKeyguardLw()); + final int fl = PolicyControl.getWindowFlags(win, attrs); if (mTopFullscreenOpaqueWindowState == null && win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD) { mForcingShowNavBar = true; mForcingShowNavBarLayer = win.getSurfaceLayer(); } + if (attrs.type == TYPE_STATUS_BAR && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + mForceStatusBarFromKeyguard = true; + } if (mTopFullscreenOpaqueWindowState == null && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) { - if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) { - if (attrs.type == TYPE_KEYGUARD) { + if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { + if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { mForceStatusBarFromKeyguard = true; } else { mForceStatusBar = true; } } - if (attrs.type == TYPE_KEYGUARD) { + if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { mShowingLockscreen = true; } boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type <= LAST_APPLICATION_WINDOW; + && attrs.type < FIRST_SYSTEM_WINDOW; if (attrs.type == TYPE_DREAM) { // If the lockscreen was showing when the dream started then wait // for the dream to draw before hiding the lockscreen. @@ -3504,38 +3798,58 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - final boolean showWhenLocked = (attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0; - final boolean dismissKeyguard = (attrs.flags & FLAG_DISMISS_KEYGUARD) != 0; + final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0; + final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0; + final boolean secureKeyguard = isKeyguardSecure(); if (appWindow) { - if (showWhenLocked || (dismissKeyguard && !isKeyguardSecure())) { - mAppsToBeHidden.remove(win.getAppToken()); + final IApplicationToken appToken = win.getAppToken(); + if (showWhenLocked) { + // Remove any previous windows with the same appToken. + mAppsToBeHidden.remove(appToken); + mAppsThatDismissKeyguard.remove(appToken); + if (mAppsToBeHidden.isEmpty() && isKeyguardSecureIncludingHidden()) { + mWinShowWhenLocked = win; + mHideLockScreen = true; + mForceStatusBarFromKeyguard = false; + } + } else if (dismissKeyguard) { + if (secureKeyguard) { + mAppsToBeHidden.add(appToken); + } else { + mAppsToBeHidden.remove(appToken); + } + mAppsThatDismissKeyguard.add(appToken); } else { - mAppsToBeHidden.add(win.getAppToken()); + mAppsToBeHidden.add(appToken); } if (attrs.x == 0 && attrs.y == 0 && attrs.width == WindowManager.LayoutParams.MATCH_PARENT && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) { if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); mTopFullscreenOpaqueWindowState = win; - if (mAppsToBeHidden.isEmpty()) { - if (showWhenLocked) { - if (DEBUG_LAYOUT) Slog.v(TAG, - "Setting mHideLockScreen to true by win " + win); - mHideLockScreen = true; - mForceStatusBarFromKeyguard = false; - } - if (dismissKeyguard && mDismissKeyguard == DISMISS_KEYGUARD_NONE) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win); - mDismissKeyguard = mWinDismissingKeyguard == win ? - DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; - mWinDismissingKeyguard = win; - mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); - } + if (!mAppsThatDismissKeyguard.isEmpty() && + mDismissKeyguard == DISMISS_KEYGUARD_NONE) { + if (DEBUG_LAYOUT) Slog.v(TAG, + "Setting mDismissKeyguard true by win " + win); + mDismissKeyguard = mWinDismissingKeyguard == win ? + DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; + mWinDismissingKeyguard = win; + mForceStatusBarFromKeyguard = mShowingLockscreen && secureKeyguard; + } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) { + if (DEBUG_LAYOUT) Slog.v(TAG, + "Setting mHideLockScreen to true by win " + win); + mHideLockScreen = true; + mForceStatusBarFromKeyguard = false; } - if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { + if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { mAllowLockscreenWhenOn = true; } } + + if (mWinShowWhenLocked != null && + mWinShowWhenLocked.getAppToken() != win.getAppToken()) { + win.hideLw(false); + } } } } @@ -3543,6 +3857,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int finishPostLayoutPolicyLw() { + if (mWinShowWhenLocked != null && + mWinShowWhenLocked != mTopFullscreenOpaqueWindowState) { + // A dialog is dismissing the keyguard. Put the wallpaper behind it and hide the + // fullscreen window. + // TODO: Make sure FLAG_SHOW_WALLPAPER is restored when dialog is dismissed. Or not. + mWinShowWhenLocked.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + mTopFullscreenOpaqueWindowState.hideLw(false); + mTopFullscreenOpaqueWindowState = mWinShowWhenLocked; + } + int changes = 0; boolean topIsFullscreen = false; @@ -3575,13 +3899,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mLastSystemUiFlags, mLastSystemUiFlags); } } else if (mTopFullscreenOpaqueWindowState != null) { + final int fl = PolicyControl.getWindowFlags(null, lp); if (localLOGV) { Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw() + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw()); Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() - + " lp.flags=0x" + Integer.toHexString(lp.flags)); + + " lp.flags=0x" + Integer.toHexString(fl)); } - topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0 + topIsFullscreen = (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0 || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; // The subtle difference between the window for mTopFullscreenOpaqueWindowState // and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window @@ -3617,11 +3942,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Hide the key guard if a visible window explicitly specifies that it wants to be // displayed when the screen is locked. - if (mKeyguard != null) { + if (mKeyguardDelegate != null && mStatusBar != null) { if (localLOGV) Slog.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard=" + mHideLockScreen); if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !isKeyguardSecure()) { - if (mKeyguard.hideLw(true)) { + mKeyguardHidden = true; + if (processKeyguardSetHiddenResultLw(mKeyguardDelegate.setOccluded(true))) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG | FINISH_LAYOUT_REDO_WALLPAPER; @@ -3635,24 +3961,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { }); } } else if (mHideLockScreen) { - if (mKeyguard.hideLw(true)) { + mKeyguardHidden = true; + if (processKeyguardSetHiddenResultLw(mKeyguardDelegate.setOccluded(true))) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG | FINISH_LAYOUT_REDO_WALLPAPER; } - if (!mShowingDream) { - mKeyguardDelegate.setHidden(true); - } } else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) { // This is the case of keyguard isSecure() and not mHideLockScreen. if (mDismissKeyguard == DISMISS_KEYGUARD_START) { // Only launch the next keyguard unlock window once per window. - if (mKeyguard.showLw(true)) { + mKeyguardHidden = false; + if (processKeyguardSetHiddenResultLw(mKeyguardDelegate.setOccluded(false))) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG | FINISH_LAYOUT_REDO_WALLPAPER; } - mKeyguardDelegate.setHidden(false); mHandler.post(new Runnable() { @Override public void run() { @@ -3662,12 +3986,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else { mWinDismissingKeyguard = null; - if (mKeyguard.showLw(true)) { + mKeyguardHidden = false; + if (processKeyguardSetHiddenResultLw(mKeyguardDelegate.setOccluded(false))) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG | FINISH_LAYOUT_REDO_WALLPAPER; } - mKeyguardDelegate.setHidden(false); } } @@ -3682,15 +4006,47 @@ public class PhoneWindowManager implements WindowManagerPolicy { return changes; } + /** + * Processes the result code of {@link IKeyguardService#setOccluded}. This is needed because we + * immediately need to put the wallpaper directly behind the Keyguard when a window with flag + * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} gets dismissed. If we + * would wait for Keyguard to change the flags, that would be running asynchronously and thus be + * too late so the user might see the window behind. + * + * @param setHiddenResult The result code from {@link IKeyguardService#setOccluded}. + * @return Whether the flags have changed and we have to redo the layout. + */ + private boolean processKeyguardSetHiddenResultLw(int setHiddenResult) { + if (setHiddenResult + == IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS) { + mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; + mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + return true; + } else if (setHiddenResult + == IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS) { + mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; + mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; + return true; + } else { + return false; + } + } + + private boolean isStatusBarKeyguard() { + return mStatusBar != null + && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; + } + + @Override public boolean allowAppAnimationsLw() { - if (mKeyguard != null && mKeyguard.isVisibleLw() && !mKeyguard.isAnimatingLw() - || mShowingDream) { + if (isStatusBarKeyguard() || mShowingDream) { // If keyguard or dreams is currently visible, no reason to animate behind it. return false; } return true; } + @Override public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { mFocusedWindow = newFocus; if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { @@ -3702,6 +4058,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { // lid changed state final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED; @@ -3720,6 +4077,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + @Override + public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) { + int lensCoverState = lensCovered ? CAMERA_LENS_COVERED : CAMERA_LENS_UNCOVERED; + if (mCameraLensCoverState == lensCoverState) { + return; + } + if (mCameraLensCoverState == CAMERA_LENS_COVERED && + lensCoverState == CAMERA_LENS_UNCOVERED) { + Intent intent; + final boolean keyguardActive = mKeyguardDelegate == null ? false : + mKeyguardDelegate.isShowing(); + if (keyguardActive) { + intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE); + } else { + intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); + } + mPowerManager.wakeUp(whenNanos / 1000000); + mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); + } + mCameraLensCoverState = lensCoverState; + } + void setHdmiPlugged(boolean plugged) { if (mHdmiPlugged != plugged) { mHdmiPlugged = plugged; @@ -3765,57 +4144,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { setHdmiPlugged(!mHdmiPlugged); } - /** - * @return Whether music is being played right now "locally" (e.g. on the device's speakers - * or wired headphones) or "remotely" (e.g. on a device using the Cast protocol and - * controlled by this device, or through remote submix). - */ - boolean isMusicActive() { - final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); - if (am == null) { - Log.w(TAG, "isMusicActive: couldn't get AudioManager reference"); - return false; - } - return am.isLocalOrRemoteMusicActive(); - } - - /** - * Tell the audio service to adjust the volume appropriate to the event. - * @param keycode - */ - void handleVolumeKey(int stream, int keycode) { - IAudioService audioService = getAudioService(); - if (audioService == null) { - return; - } - try { - // when audio is playing locally, we shouldn't have to hold a wake lock - // during the call, but we do it as a precaution for the rare possibility - // that the music stops right before we call this. - // Otherwise we might also be in a remote playback case. - // TODO: Actually handle MUTE. - mBroadcastWakeLock.acquire(); - if (stream == AudioSystem.STREAM_MUSIC) { - audioService.adjustLocalOrRemoteStreamVolume(stream, - keycode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER, - mContext.getOpPackageName()); - } else { - audioService.adjustStreamVolume(stream, - keycode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER, - 0, - mContext.getOpPackageName()); - } - } catch (RemoteException e) { - Log.w(TAG, "IAudioService.adjust*StreamVolume() threw RemoteException " + e); - } finally { - mBroadcastWakeLock.release(); - } - } - final Object mScreenshotLock = new Object(); ServiceConnection mScreenshotConnection = null; @@ -3906,15 +4234,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // when the keyguard is hidden by another activity. final boolean keyguardActive = (mKeyguardDelegate == null ? false : (interactive ? - mKeyguardDelegate.isShowingAndNotHidden() : + mKeyguardDelegate.isShowingAndNotOccluded() : mKeyguardDelegate.isShowing())); - if (keyCode == KeyEvent.KEYCODE_POWER - || keyCode == KeyEvent.KEYCODE_SLEEP - || keyCode == KeyEvent.KEYCODE_WAKEUP) { - policyFlags |= WindowManagerPolicy.FLAG_WAKE; - } - if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTq keycode=" + keyCode + " interactive=" + interactive + " keyguardActive=" + keyguardActive @@ -3923,10 +4245,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Basic policy based on interactive state. int result; - boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE - | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; + boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 + || event.isWakeKey(); if (interactive || (isInjected && !isWakeKey)) { - // When the screen is on or if the key is injected pass the key to the application. + // When the device is interactive or the key is injected pass the + // key to the application. + result = ACTION_PASS_TO_USER; + isWakeKey = false; + } else if (!interactive && shouldDispatchInputWhenNonInteractive()) { + // If we're currently dozing with the screen on and the keyguard showing, pass the key + // to the application but preserve its wake key status to make sure we still move + // from dozing to fully interactive if we would normally go from off to fully + // interactive. result = ACTION_PASS_TO_USER; } else { // When the screen is off and the key is not injected, determine whether @@ -3940,6 +4270,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If the key would be handled globally, just return the result, don't worry about special // key processing. if (mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) { + if (isWakeKey) { + mPowerManager.wakeUp(event.getEventTime()); + } return result; } @@ -3980,44 +4313,44 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - // If an incoming call is ringing, either VOLUME key means - // "silence ringer". We handle these keys here, rather than - // in the InCallScreen, to make sure we'll respond to them - // even if the InCallScreen hasn't come to the foreground yet. - // Look for the DOWN event here, to agree with the "fallback" - // behavior in the InCallScreen. - Log.i(TAG, "interceptKeyBeforeQueueing:" - + " VOLUME key-down while ringing: Silence ringer!"); - - // Silence the ringer. (It's safe to call this - // even if the ringer has already been silenced.) - telephonyService.silenceRinger(); - - // And *don't* pass this key thru to the current activity - // (which is probably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - break; - } - if (telephonyService.isOffhook() - && (result & ACTION_PASS_TO_USER) == 0) { - // If we are in call but we decided not to pass the key to - // the application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - break; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + TelecomManager telecomManager = getTelecommService(); + if (telecomManager != null) { + if (telecomManager.isRinging()) { + // If an incoming call is ringing, either VOLUME key means + // "silence ringer". We handle these keys here, rather than + // in the InCallScreen, to make sure we'll respond to them + // even if the InCallScreen hasn't come to the foreground yet. + // Look for the DOWN event here, to agree with the "fallback" + // behavior in the InCallScreen. + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " VOLUME key-down while ringing: Silence ringer!"); + + // Silence the ringer. (It's safe to call this + // even if the ringer has already been silenced.) + telecomManager.silenceRinger(); + + // And *don't* pass this key thru to the current activity + // (which is probably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + break; + } + if (telecomManager.isInCall() + && (result & ACTION_PASS_TO_USER) == 0) { + // If we are in call but we decided not to pass the key to + // the application, just pass it to the session service. + + MediaSessionLegacyHelper.getHelper(mContext) + .sendVolumeKeyEvent(event, false); + break; } } - if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { - // If music is playing but we decided not to pass the key to the - // application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + if ((result & ACTION_PASS_TO_USER) == 0) { + // If we aren't passing to the user and no one else + // handled it send it to the session manager to figure + // out. + MediaSessionLegacyHelper.getHelper(mContext) + .sendVolumeKeyEvent(event, true); break; } } @@ -4027,14 +4360,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_ENDCALL: { result &= ~ACTION_PASS_TO_USER; if (down) { - ITelephony telephonyService = getTelephonyService(); + TelecomManager telecomManager = getTelecommService(); boolean hungUp = false; - if (telephonyService != null) { - try { - hungUp = telephonyService.endCall(); - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); - } + if (telecomManager != null) { + hungUp = telecomManager.endCall(); } interceptPowerKeyDown(!interactive || hungUp); } else { @@ -4047,7 +4376,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { - mPowerManager.goToSleep(event.getEventTime()); + mPowerManager.goToSleep(event.getEventTime(), + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); isWakeKey = false; } } @@ -4058,8 +4388,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { - mImmersiveModeConfirmation.onPowerKeyDown(interactive, event.getDownTime(), - isImmersiveMode(mLastSystemUiFlags)); + boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive, + event.getDownTime(), isImmersiveMode(mLastSystemUiFlags)); + if (panic) { + mHandler.post(mRequestTransientNav); + } if (interactive && !mPowerKeyTriggered && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { mPowerKeyTriggered = true; @@ -4067,23 +4400,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { interceptScreenshotChord(); } - ITelephony telephonyService = getTelephonyService(); + TelecomManager telecomManager = getTelecommService(); boolean hungUp = false; - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - // Pressing Power while there's a ringing incoming - // call should silence the ringer. - telephonyService.silenceRinger(); - } else if ((mIncallPowerBehavior - & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 - && telephonyService.isOffhook() && interactive) { - // Otherwise, if "Power button ends call" is enabled, - // the Power button will hang up any current active call. - hungUp = telephonyService.endCall(); - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + if (telecomManager != null) { + if (telecomManager.isRinging()) { + // Pressing Power while there's a ringing incoming + // call should silence the ringer. + telecomManager.silenceRinger(); + } else if ((mIncallPowerBehavior + & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 + && telecomManager.isInCall() && interactive) { + // Otherwise, if "Power button ends call" is enabled, + // the Power button will hang up any current active call. + hungUp = telecomManager.endCall(); } } interceptPowerKeyDown(!interactive || hungUp @@ -4105,33 +4434,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!mPowerManager.isInteractive()) { useHapticFeedback = false; // suppress feedback if already non-interactive } - mPowerManager.goToSleep(event.getEventTime()); + mPowerManager.goToSleep(event.getEventTime(), + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); isWakeKey = false; break; } case KeyEvent.KEYCODE_WAKEUP: { result &= ~ACTION_PASS_TO_USER; + isWakeKey = true; break; } case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (!telephonyService.isIdle()) { - // Suppress PLAY/PAUSE toggle when phone is ringing or in-call - // to avoid music playback. - break; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); - } - } - } case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_MEDIA_STOP: @@ -4141,6 +4458,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) { + // If the global session is active pass all media keys to it + // instead of the active window. + result &= ~ACTION_PASS_TO_USER; + } if ((result & ACTION_PASS_TO_USER) == 0) { // Only do this if we would otherwise not pass it to the user. In that // case, the PhoneWindow class will do the same thing, except it will @@ -4158,25 +4480,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_CALL: { if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - Log.i(TAG, "interceptKeyBeforeQueueing:" - + " CALL key-down while ringing: Answer the call!"); - telephonyService.answerRingingCall(); - - // And *don't* pass this key thru to the current activity - // (which is presumably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + TelecomManager telecomManager = getTelecommService(); + if (telecomManager != null) { + if (telecomManager.isRinging()) { + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " CALL key-down while ringing: Answer the call!"); + telecomManager.acceptRingingCall(); + + // And *don't* pass this key thru to the current activity + // (which is presumably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; } } } break; } + case KeyEvent.KEYCODE_VOICE_ASSIST: { + // Only do this if we would otherwise not pass it to the user. In that case, + // interceptKeyBeforeDispatching would apply a similar but different policy in + // order to invoke voice assist actions. Note that we need to make a copy of the + // key event here because the original key event will be recycled when we return. + if ((result & ACTION_PASS_TO_USER) == 0 && !down) { + mBroadcastWakeLock.acquire(); + Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK, + keyguardActive ? 1 : 0, 0); + msg.setAsynchronous(true); + msg.sendToTarget(); + } + } } if (useHapticFeedback) { @@ -4226,14 +4557,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public int interceptWakeMotionBeforeQueueing(long whenNanos, int policyFlags) { - // We already know this is a wake motion so just wake up. - // Note that we would observe policyFlags containing - // FLAG_WAKE and FLAG_INTERACTIVE here. - mPowerManager.wakeUp(whenNanos / 1000000); + public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + if ((policyFlags & FLAG_WAKE) != 0) { + mPowerManager.wakeUp(whenNanos / 1000000); + return 0; + } + if (shouldDispatchInputWhenNonInteractive()) { + return ACTION_PASS_TO_USER; + } return 0; } + private boolean shouldDispatchInputWhenNonInteractive() { + return keyguardIsShowingTq() && mDisplay != null && + mDisplay.getState() != Display.STATE_OFF; + } + void dispatchMediaKeyWithWakeLock(KeyEvent event) { if (DEBUG_INPUT) { Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event); @@ -4279,17 +4618,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) { if (ActivityManagerNative.isSystemReady()) { - IAudioService audioService = getAudioService(); - if (audioService != null) { - try { - audioService.dispatchMediaKeyEventUnderWakelock(event); - } catch (RemoteException e) { - Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e); - } - } + MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true); } } + void launchVoiceAssistWithWakeLock(boolean keyguardActive) { + Intent voiceIntent = + new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); + voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, keyguardActive); + mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF); + mBroadcastWakeLock.release(); + } + BroadcastReceiver mDockReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -4347,8 +4687,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + private final Runnable mRequestTransientNav = new Runnable() { + @Override + public void run() { + requestTransientBars(mNavigationBar); + } + }; + private void requestTransientBars(WindowState swipeTarget) { synchronized (mWindowManagerFuncs.getWindowManagerLock()) { + if (!isUserSetupComplete()) { + // Swipe-up for navigation bar is disabled during setup + return; + } boolean sb = mStatusBarController.checkShowTransientBarLw(); boolean nb = mNavigationBarController.checkShowTransientBarLw(); if (sb || nb) { @@ -4365,89 +4716,187 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Called on the PowerManager's Notifier thread. @Override public void goingToSleep(int why) { EventLog.writeEvent(70000, 0); + if (DEBUG_WAKEUP) Slog.i(TAG, "Going to sleep..."); + + // We must get this work done here because the power manager will drop + // the wake lock and let the system suspend once this function returns. synchronized (mLock) { - mScreenOnEarly = false; - mScreenOnFully = false; + mAwake = false; + mKeyguardDrawComplete = false; + updateWakeGestureListenerLp(); + updateOrientationListenerLp(); + updateLockScreenTimeout(); } + if (mKeyguardDelegate != null) { mKeyguardDelegate.onScreenTurnedOff(why); } - synchronized (mLock) { - updateOrientationListenerLp(); - updateLockScreenTimeout(); - } } + // Called on the PowerManager's Notifier thread. @Override - public void wakingUp(final ScreenOnListener screenOnListener) { + public void wakingUp() { EventLog.writeEvent(70000, 1); - if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...", - new RuntimeException("here").fillInStackTrace()); - mHandler.obtainMessage(MSG_WAKING_UP, screenOnListener).sendToTarget(); - } - - // Called on the mHandler thread. - private void handleWakingUp(final ScreenOnListener screenOnListener) { - if (screenOnListener != null) { - mScreenOnListeners.add(screenOnListener); - } + if (DEBUG_WAKEUP) Slog.i(TAG, "Waking up..."); + // Since goToSleep performs these functions synchronously, we must + // do the same here. We cannot post this work to a handler because + // that might cause it to become reordered with respect to what + // may happen in a future call to goToSleep. synchronized (mLock) { - mScreenOnEarly = true; + mAwake = true; + mKeyguardDrawComplete = false; + if (mKeyguardDelegate != null) { + mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000); + } + + updateWakeGestureListenerLp(); updateOrientationListenerLp(); updateLockScreenTimeout(); } - mKeyguardDrawComplete = false; - mWindowManagerDrawComplete = false; if (mKeyguardDelegate != null) { - mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000); mKeyguardDelegate.onScreenTurnedOn(mKeyguardDelegateCallback); + // ... eventually calls finishKeyguardDrawn } else { if (DEBUG_WAKEUP) Slog.d(TAG, "null mKeyguardDelegate: setting mKeyguardDrawComplete."); + finishKeyguardDrawn(); + } + } + + private void finishKeyguardDrawn() { + synchronized (mLock) { + if (!mAwake || mKeyguardDrawComplete) { + return; // spurious + } + mKeyguardDrawComplete = true; + if (mKeyguardDelegate != null) { + mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); + } } - mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, 500); + + finishScreenTurningOn(); } - // Called on the mHandler thread. - private void finishScreenTurningOn() { - if (DEBUG_WAKEUP) Slog.d(TAG, - "finishScreenTurningOn: mKeyguardDrawComplete=" + mKeyguardDrawComplete - + " mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); - if (!mKeyguardDrawComplete || !mWindowManagerDrawComplete) { - return; + // Called on the DisplayManager's DisplayPowerController thread. + @Override + public void screenTurnedOff() { + if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off..."); + + synchronized (mLock) { + mScreenOnEarly = false; + mScreenOnFully = false; + mWindowManagerDrawComplete = false; + mScreenOnListener = null; + updateOrientationListenerLp(); + } + } + + // Called on the DisplayManager's DisplayPowerController thread. + @Override + public void screenTurningOn(final ScreenOnListener screenOnListener) { + if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on..."); + + synchronized (mLock) { + mScreenOnEarly = true; + mScreenOnFully = false; + mWindowManagerDrawComplete = false; + mScreenOnListener = screenOnListener; + updateOrientationListenerLp(); } + mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, + WAITING_FOR_DRAWN_TIMEOUT); + // ... eventually calls finishWindowsDrawn + } + + private void finishWindowsDrawn() { synchronized (mLock) { + if (!mScreenOnEarly || mWindowManagerDrawComplete) { + return; // spurious + } + + mWindowManagerDrawComplete = true; + } + + finishScreenTurningOn(); + } + + private void finishScreenTurningOn() { + final ScreenOnListener listener; + final boolean enableScreen; + synchronized (mLock) { + if (DEBUG_WAKEUP) Slog.d(TAG, + "finishScreenTurningOn: mAwake=" + mAwake + + ", mScreenOnEarly=" + mScreenOnEarly + + ", mScreenOnFully=" + mScreenOnFully + + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete + + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); + + if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete + || (mAwake && !mKeyguardDrawComplete)) { + return; // spurious or not ready yet + } + + if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on..."); + listener = mScreenOnListener; + mScreenOnListener = null; mScreenOnFully = true; + + // Remember the first time we draw the keyguard so we know when we're done with + // the main part of booting and can enable the screen and hide boot messages. + if (!mKeyguardDrawnOnce && mAwake) { + mKeyguardDrawnOnce = true; + enableScreen = true; + if (mBootMessageNeedsHiding) { + mBootMessageNeedsHiding = false; + hideBootMessages(); + } + } else { + enableScreen = false; + } } - try { - mWindowManager.setEventDispatching(true); - } catch (RemoteException unhandled) { + if (listener != null) { + listener.onScreenOn(); } - for (int i = mScreenOnListeners.size() - 1; i >=0; --i) { - mScreenOnListeners.remove(i).onScreenOn(); + if (enableScreen) { + try { + mWindowManager.enableScreenIfNeeded(); + } catch (RemoteException unhandled) { + } } } - @Override - public boolean isScreenOnEarly() { - return mScreenOnEarly; + private void handleHideBootMessage() { + synchronized (mLock) { + if (!mKeyguardDrawnOnce) { + mBootMessageNeedsHiding = true; + return; // keyguard hasn't drawn the first time yet, not done booting + } + } + + if (mBootMsgDialog != null) { + if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: dismissing"); + mBootMsgDialog.dismiss(); + mBootMsgDialog = null; + } } @Override - public boolean isScreenOnFully() { + public boolean isScreenOn() { return mScreenOnFully; } /** {@inheritDoc} */ + @Override public void enableKeyguard(boolean enabled) { if (mKeyguardDelegate != null) { mKeyguardDelegate.setKeyguardEnabled(enabled); @@ -4455,6 +4904,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void exitKeyguardSecurely(OnKeyguardExitResult callback) { if (mKeyguardDelegate != null) { mKeyguardDelegate.verifyUnlock(callback); @@ -4463,43 +4913,73 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean keyguardIsShowingTq() { if (mKeyguardDelegate == null) return false; - return mKeyguardDelegate.isShowingAndNotHidden(); + return mKeyguardDelegate.isShowingAndNotOccluded(); } /** {@inheritDoc} */ + @Override public boolean isKeyguardLocked() { return keyguardOn(); } /** {@inheritDoc} */ + @Override public boolean isKeyguardSecure() { if (mKeyguardDelegate == null) return false; return mKeyguardDelegate.isSecure(); } + // Returns true if keyguard is currently locked whether or not it is currently hidden. + private boolean isKeyguardSecureIncludingHidden() { + return mKeyguardDelegate.isSecure() && mKeyguardDelegate.isShowing(); + } + /** {@inheritDoc} */ + @Override public boolean inKeyguardRestrictedKeyInputMode() { if (mKeyguardDelegate == null) return false; return mKeyguardDelegate.isInputRestricted(); } + @Override public void dismissKeyguardLw() { - if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) { + if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) { mHandler.post(new Runnable() { + @Override public void run() { - if (mKeyguardDelegate.isDismissable()) { - // Can we just finish the keyguard straight away? - mKeyguardDelegate.keyguardDone(false, true); - } else { - // ask the keyguard to prompt the user to authenticate if necessary - mKeyguardDelegate.dismiss(); - } + // ask the keyguard to prompt the user to authenticate if necessary + mKeyguardDelegate.dismiss(); } }); } } + public void notifyActivityDrawnForKeyguardLw() { + if (mKeyguardDelegate != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mKeyguardDelegate.onActivityDrawn(); + } + }); + } + } + + @Override + public boolean isKeyguardDrawnLw() { + synchronized (mLock) { + return mKeyguardDrawnOnce; + } + } + + @Override + public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + if (mKeyguardDelegate != null) { + mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration); + } + } + void sendCloseSystemWindows() { sendCloseSystemWindows(mContext, null); } @@ -4709,6 +5189,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return rotation == mPortraitRotation || rotation == mUpsideDownRotation; } + @Override public int getUserRotationMode() { return Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ? @@ -4717,6 +5198,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // User rotation: to be used when all else fails in assigning an orientation to the device + @Override public void setUserRotationMode(int mode, int rot) { ContentResolver res = mContext.getContentResolver(); @@ -4738,6 +5220,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + @Override public void setSafeMode(boolean safeMode) { mSafeMode = safeMode; performHapticFeedbackLw(null, safeMode @@ -4763,6 +5246,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate = new KeyguardServiceDelegate(mContext, null); mKeyguardDelegate.onSystemReady(); + readCameraLensCoverState(); updateUiMode(); synchronized (mLock) { updateOrientationListenerLp(); @@ -4777,25 +5261,37 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void systemBooted() { if (mKeyguardDelegate != null) { + mKeyguardDelegate.bindService(mContext); mKeyguardDelegate.onBootCompleted(); } synchronized (mLock) { mSystemBooted = true; } + wakingUp(); + screenTurningOn(null); } ProgressDialog mBootMsgDialog = null; /** {@inheritDoc} */ + @Override public void showBootMessage(final CharSequence msg, final boolean always) { mHandler.post(new Runnable() { @Override public void run() { if (mBootMsgDialog == null) { - int theme = mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WATCH) ? - com.android.internal.R.style.Theme_Micro_Dialog_Alert : 0; + int theme; + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH)) { + theme = com.android.internal.R.style.Theme_Micro_Dialog_Alert; + } else if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEVISION)) { + theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert; + } else { + theme = 0; + } mBootMsgDialog = new ProgressDialog(mContext, theme) { // This dialog will consume all events coming in to @@ -4841,18 +5337,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void hideBootMessages() { - mHandler.post(new Runnable() { - @Override public void run() { - if (mBootMsgDialog != null) { - mBootMsgDialog.dismiss(); - mBootMsgDialog = null; - } - } - }); + mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE); } /** {@inheritDoc} */ + @Override public void userActivity() { // *************************************** // NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE @@ -4896,6 +5387,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout(); + @Override public void lockNow(Bundle options) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); mHandler.removeCallbacks(mScreenLockTimeout); @@ -4909,7 +5401,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void updateLockScreenTimeout() { synchronized (mScreenLockTimeout) { - boolean enable = (mAllowLockscreenWhenOn && mScreenOnEarly && + boolean enable = (mAllowLockscreenWhenOn && mAwake && mKeyguardDelegate != null && mKeyguardDelegate.isSecure()); if (mLockScreenTimerActive != enable) { if (enable) { @@ -4935,9 +5427,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void applyLidSwitchState() { if (mLidState == LID_CLOSED && mLidControlsSleep) { mPowerManager.goToSleep(SystemClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_USER, + PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); } + + synchronized (mLock) { + updateWakeGestureListenerLp(); + } } void updateUiMode() { @@ -5072,7 +5568,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { dock.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, - null, null, null, UserHandle.USER_CURRENT); + null, null, UserHandle.USER_CURRENT); if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) { return false; } @@ -5083,7 +5579,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, - null, null, null, UserHandle.USER_CURRENT); + null, null, UserHandle.USER_CURRENT); if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) { return false; } @@ -5131,7 +5627,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0; - if (!always && (hapticsDisabled || mKeyguardDelegate.isShowingAndNotHidden())) { + if (hapticsDisabled && !always) { return false; } long[] pattern = null; @@ -5145,6 +5641,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { case HapticFeedbackConstants.KEYBOARD_TAP: pattern = mKeyboardTapVibePattern; break; + case HapticFeedbackConstants.CLOCK_TICK: + pattern = mClockTickVibePattern; + break; + case HapticFeedbackConstants.CALENDAR_DATE: + pattern = mCalendarDateVibePattern; + break; case HapticFeedbackConstants.SAFE_MODE_DISABLED: pattern = mSafeModeDisabledVibePattern; break; @@ -5165,10 +5667,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (pattern.length == 1) { // One-shot vibration - mVibrator.vibrate(owningUid, owningPackage, pattern[0]); + mVibrator.vibrate(owningUid, owningPackage, pattern[0], VIBRATION_ATTRIBUTES); } else { // Pattern vibration - mVibrator.vibrate(owningUid, owningPackage, pattern, -1); + mVibrator.vibrate(owningUid, owningPackage, pattern, -1, VIBRATION_ATTRIBUTES); } return true; } @@ -5179,8 +5681,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void keepScreenOnStoppedLw() { - if (mKeyguardDelegate != null && !mKeyguardDelegate.isShowingAndNotHidden()) { - long curTime = SystemClock.uptimeMillis(); + if (mKeyguardDelegate != null && !mKeyguardDelegate.isShowingAndNotOccluded()) { mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } } @@ -5192,7 +5693,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (win == null) { return 0; } - if (win.getAttrs().type == TYPE_KEYGUARD && mHideLockScreen == true) { + if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) { // We are updating at a point where the keyguard has gotten // focus, but we were last in a state where the top window is // hiding it. This is probably because the keyguard as been @@ -5202,11 +5703,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - int tmpVisibility = win.getSystemUiVisibility() + int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) & ~mResettingSystemUiFlags & ~mForceClearedSystemUiFlags; if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { - tmpVisibility &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; + tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); } final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); final int diff = visibility ^ mLastSystemUiFlags; @@ -5238,26 +5739,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { // apply translucent bar vis flags - WindowState transWin = mKeyguard != null && mKeyguard.isVisibleLw() && !mHideLockScreen - ? mKeyguard + WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen + ? mStatusBar : mTopFullscreenOpaqueWindowState; vis = mStatusBarController.applyTranslucentFlagLw(transWin, vis, oldVis); vis = mNavigationBarController.applyTranslucentFlagLw(transWin, vis, oldVis); // prevent status bar interaction from clearing certain flags boolean statusBarHasFocus = win.getAttrs().type == TYPE_STATUS_BAR; - if (statusBarHasFocus) { + if (statusBarHasFocus && !isStatusBarKeyguard()) { int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.STATUS_BAR_TRANSLUCENT - | View.NAVIGATION_BAR_TRANSLUCENT; + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + if (mHideLockScreen) { + flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; + } vis = (vis & ~flags) | (oldVis & flags); } - if (!areTranslucentBarsAllowed()) { - vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT); + if (!areTranslucentBarsAllowed() && transWin != mStatusBar) { + vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT + | View.SYSTEM_UI_TRANSPARENT); } // update status bar @@ -5265,7 +5768,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean hideStatusBarWM = mTopFullscreenOpaqueWindowState != null && - (mTopFullscreenOpaqueWindowState.getAttrs().flags + (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; boolean hideStatusBarSysui = (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; @@ -5298,7 +5801,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean newImmersiveMode = isImmersiveMode(vis); if (win != null && oldImmersiveMode != newImmersiveMode) { final String pkg = win.getOwningPackage(); - mImmersiveModeConfirmation.immersiveModeChanged(pkg, newImmersiveMode); + mImmersiveModeConfirmation.immersiveModeChanged(pkg, newImmersiveMode, + isUserSetupComplete()); } vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); @@ -5329,7 +5833,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * R.boolean.config_enableTranslucentDecor is false. */ private boolean areTranslucentBarsAllowed() { - return mTranslucentDecorEnabled && !mTouchExplorationEnabled; + return mTranslucentDecorEnabled + && !mAccessibilityManager.isTouchExplorationEnabled(); } // Use this instead of checking config_showNavigationBar so that it can be consistently @@ -5346,6 +5851,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public int getInputMethodWindowVisibleHeightLw() { + return mDockBottom - mCurBottom; + } + + @Override public void setCurrentUserLw(int newUserId) { mCurrentUserId = newUserId; if (mKeyguardDelegate != null) { @@ -5375,11 +5885,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void setTouchExplorationEnabled(boolean enabled) { - mTouchExplorationEnabled = enabled; - } - - @Override public boolean isTopLevelWindow(int windowType) { if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { @@ -5395,6 +5900,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mSystemBooted="); pw.println(mSystemBooted); pw.print(prefix); pw.print("mLidState="); pw.print(mLidState); pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation); + pw.print(" mCameraLensCoverState="); pw.print(mCameraLensCoverState); pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged); if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 || mForceClearedSystemUiFlags != 0) { @@ -5409,6 +5915,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu); } + pw.print(prefix); pw.print("mWakeGestureEnabledSetting="); + pw.println(mWakeGestureEnabledSetting); + pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation); pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode); pw.print(" mDockMode="); pw.print(mDockMode); @@ -5430,9 +5939,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior); pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior); pw.print(prefix); pw.print("mHasSoftInput="); pw.println(mHasSoftInput); + pw.print(prefix); pw.print("mAwake="); pw.println(mAwake); pw.print(prefix); pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly); - pw.print(" mScreenOnFully="); pw.print(mScreenOnFully); - pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled); + pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); + pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); + pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); + pw.print(prefix); pw.print("mOrientationSensorEnabled="); + pw.println(mOrientationSensorEnabled); pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft); pw.print(","); pw.print(mOverscanScreenTop); pw.print(") "); pw.print(mOverscanScreenWidth); @@ -5477,6 +5990,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(","); pw.print(mContentTop); pw.print(")-("); pw.print(mContentRight); pw.print(","); pw.print(mContentBottom); pw.println(")"); + pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft); + pw.print(","); pw.print(mVoiceContentTop); + pw.print(")-("); pw.print(mVoiceContentRight); + pw.print(","); pw.print(mVoiceContentBottom); pw.println(")"); pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft); pw.print(","); pw.print(mDockTop); pw.print(")-("); pw.print(mDockRight); @@ -5497,15 +6014,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.println(mStatusBar); + pw.print(prefix); pw.print("isStatusBarKeyguard="); + pw.print(isStatusBarKeyguard()); } if (mNavigationBar != null) { pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar); } - if (mKeyguard != null) { - pw.print(prefix); pw.print("mKeyguard="); - pw.println(mKeyguard); - } if (mFocusedWindow != null) { pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); @@ -5548,7 +6063,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation); pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); + + mGlobalKeyManager.dump(prefix, pw); mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); + PolicyControl.dump(prefix, pw); + + if (mWakeGestureListener != null) { + mWakeGestureListener.dump(pw, prefix); + } + if (mOrientationListener != null) { + mOrientationListener.dump(pw, prefix); + } } } diff --git a/policy/src/com/android/internal/policy/impl/PolicyControl.java b/policy/src/com/android/internal/policy/impl/PolicyControl.java new file mode 100644 index 0000000..9abd906 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/PolicyControl.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2014 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.internal.policy.impl; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Slog; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerPolicy.WindowState; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Runtime adjustments applied to the global window policy. + * + * This includes forcing immersive mode behavior for one or both system bars (based on a package + * list) and permanently disabling immersive mode confirmations for specific packages. + * + * Control by setting {@link Settings.Global.POLICY_CONTROL} to one or more name-value pairs. + * e.g. + * to force immersive mode everywhere: + * "immersive.full=*" + * to force transient status for all apps except a specific package: + * "immersive.status=apps,-com.package" + * to disable the immersive mode confirmations for specific packages: + * "immersive.preconfirms=com.package.one,com.package.two" + * + * Separate multiple name-value pairs with ':' + * e.g. "immersive.status=apps:immersive.preconfirms=*" + */ +public class PolicyControl { + private static String TAG = "PolicyControl"; + private static boolean DEBUG = false; + + private static final String NAME_IMMERSIVE_FULL = "immersive.full"; + private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; + private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation"; + private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms"; + + private static String sSettingValue; + private static Filter sImmersivePreconfirmationsFilter; + private static Filter sImmersiveStatusFilter; + private static Filter sImmersiveNavigationFilter; + + public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) { + attrs = attrs != null ? attrs : win.getAttrs(); + int vis = win != null ? win.getSystemUiVisibility() : attrs.systemUiVisibility; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.STATUS_BAR_TRANSLUCENT); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.NAVIGATION_BAR_TRANSLUCENT); + } + return vis; + } + + public static int getWindowFlags(WindowState win, LayoutParams attrs) { + attrs = attrs != null ? attrs : win.getAttrs(); + int flags = attrs.flags; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { + flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { + flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + } + return flags; + } + + public static int adjustClearableFlags(WindowState win, int clearableFlags) { + final LayoutParams attrs = win != null ? win.getAttrs() : null; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { + clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + } + return clearableFlags; + } + + public static boolean disableImmersiveConfirmation(String pkg) { + return (sImmersivePreconfirmationsFilter != null + && sImmersivePreconfirmationsFilter.matches(pkg)) + || ActivityManager.isRunningInTestHarness(); + } + + public static void reloadFromSetting(Context context) { + if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); + String value = null; + try { + value = Settings.Global.getStringForUser(context.getContentResolver(), + Settings.Global.POLICY_CONTROL, + UserHandle.USER_CURRENT); + if (sSettingValue != null && sSettingValue.equals(value)) return; + setFilters(value); + sSettingValue = value; + } catch (Throwable t) { + Slog.w(TAG, "Error loading policy control, value=" + value, t); + } + } + + public static void dump(String prefix, PrintWriter pw) { + dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); + dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); + dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); + } + + private static void dump(String name, Filter filter, String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('='); + if (filter == null) { + pw.println("null"); + } else { + filter.dump(pw); pw.println(); + } + } + + private static void setFilters(String value) { + if (DEBUG) Slog.d(TAG, "setFilters: " + value); + sImmersiveStatusFilter = null; + sImmersiveNavigationFilter = null; + sImmersivePreconfirmationsFilter = null; + if (value != null) { + String[] nvps = value.split(":"); + for (String nvp : nvps) { + int i = nvp.indexOf('='); + if (i == -1) continue; + String n = nvp.substring(0, i); + String v = nvp.substring(i + 1); + if (n.equals(NAME_IMMERSIVE_FULL)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = sImmersiveNavigationFilter = f; + if (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = f; + } + } else if (n.equals(NAME_IMMERSIVE_STATUS)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = f; + } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) { + Filter f = Filter.parse(v); + sImmersiveNavigationFilter = f; + if (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = f; + } + } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) { + Filter f = Filter.parse(v); + sImmersivePreconfirmationsFilter = f; + } + } + } + if (DEBUG) { + Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); + Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); + Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter); + } + } + + private static class Filter { + private static final String ALL = "*"; + private static final String APPS = "apps"; + + private final ArraySet<String> mWhitelist; + private final ArraySet<String> mBlacklist; + + private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { + mWhitelist = whitelist; + mBlacklist = blacklist; + } + + boolean matches(LayoutParams attrs) { + if (attrs == null) return false; + boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + if (isApp && mBlacklist.contains(APPS)) return false; + if (onBlacklist(attrs.packageName)) return false; + if (isApp && mWhitelist.contains(APPS)) return true; + return onWhitelist(attrs.packageName); + } + + boolean matches(String packageName) { + return !onBlacklist(packageName) && onWhitelist(packageName); + } + + private boolean onBlacklist(String packageName) { + return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + } + + private boolean onWhitelist(String packageName) { + return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + } + + void dump(PrintWriter pw) { + pw.print("Filter["); + dump("whitelist", mWhitelist, pw); pw.print(','); + dump("blacklist", mBlacklist, pw); pw.print(']'); + } + + private void dump(String name, ArraySet<String> set, PrintWriter pw) { + pw.print(name); pw.print("=("); + final int n = set.size(); + for (int i = 0; i < n; i++) { + if (i > 0) pw.print(','); + pw.print(set.valueAt(i)); + } + pw.print(')'); + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + dump(new PrintWriter(sw, true)); + return sw.toString(); + } + + // value = comma-delimited list of tokens, where token = (package name|apps|*) + // e.g. "com.package1", or "apps, com.android.keyguard" or "*" + static Filter parse(String value) { + if (value == null) return null; + ArraySet<String> whitelist = new ArraySet<String>(); + ArraySet<String> blacklist = new ArraySet<String>(); + for (String token : value.split(",")) { + token = token.trim(); + if (token.startsWith("-") && token.length() > 1) { + token = token.substring(1); + blacklist.add(token); + } else { + whitelist.add(token); + } + } + return new Filter(whitelist, blacklist); + } + } +} diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java index 8d87728..3490bd4 100644 --- a/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java +++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java @@ -21,7 +21,6 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.LinearLayout; diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java index 2f0d7d6..bc55ed1 100644 --- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java +++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java @@ -123,7 +123,7 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { + public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_TAB) { // Ignore all meta keys other than SHIFT. The app switch key could be a // fallback action chorded with ALT, META or even CTRL depending on the key map. diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java index 75a1b01..bb898f7 100644 --- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java +++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java @@ -25,7 +25,6 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; import android.view.KeyCharacterMap; -import android.view.KeyEvent; import java.net.URISyntaxException; diff --git a/policy/src/com/android/internal/policy/impl/WakeGestureListener.java b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java new file mode 100644 index 0000000..9396c2c --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 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.internal.policy.impl; + +import android.os.Handler; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.TriggerEvent; +import android.hardware.TriggerEventListener; + +import java.io.PrintWriter; + +/** + * Watches for wake gesture sensor events then invokes the listener. + */ +public abstract class WakeGestureListener { + private static final String TAG = "WakeGestureListener"; + + private final SensorManager mSensorManager; + private final Handler mHandler; + + private final Object mLock = new Object(); + + private boolean mTriggerRequested; + private Sensor mSensor; + + public WakeGestureListener(Context context, Handler handler) { + mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + mHandler = handler; + + mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_WAKE_GESTURE); + } + + public abstract void onWakeUp(); + + public boolean isSupported() { + synchronized (mLock) { + return mSensor != null; + } + } + + public void requestWakeUpTrigger() { + synchronized (mLock) { + if (mSensor != null && !mTriggerRequested) { + mTriggerRequested = true; + mSensorManager.requestTriggerSensor(mListener, mSensor); + } + } + } + + public void cancelWakeUpTrigger() { + synchronized (mLock) { + if (mSensor != null && mTriggerRequested) { + mTriggerRequested = false; + mSensorManager.cancelTriggerSensor(mListener, mSensor); + } + } + } + + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "mTriggerRequested=" + mTriggerRequested); + pw.println(prefix + "mSensor=" + mSensor); + } + } + + private final TriggerEventListener mListener = new TriggerEventListener() { + @Override + public void onTrigger(TriggerEvent event) { + synchronized (mLock) { + mTriggerRequested = false; + mHandler.post(mWakeUpRunnable); + } + } + }; + + private final Runnable mWakeUpRunnable = new Runnable() { + @Override + public void run() { + onWakeUp(); + } + }; +} diff --git a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java index 0c77556..704da33 100644 --- a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java +++ b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java @@ -26,6 +26,9 @@ import android.os.SystemProperties; import android.util.FloatMath; import android.util.Log; import android.util.Slog; +import android.util.TimeUtils; + +import java.io.PrintWriter; /** * A special helper class used by the WindowManager @@ -181,6 +184,21 @@ public abstract class WindowOrientationListener { */ public abstract void onProposedRotationChanged(int rotation); + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "mEnabled=" + mEnabled); + pw.println(prefix + "mCurrentRotation=" + mCurrentRotation); + pw.println(prefix + "mSensor=" + mSensor); + pw.println(prefix + "mRate=" + mRate); + + if (mSensorEventListener != null) { + mSensorEventListener.dumpLocked(pw, prefix); + } + } + } + /** * This class filters the raw accelerometer data and tries to detect actual changes in * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, @@ -342,6 +360,14 @@ public abstract class WindowOrientationListener { /* ROTATION_270 */ { -25, 65 } }; + // The tilt angle below which we conclude that the user is holding the device + // overhead reading in bed and lock into that state. + private final int TILT_OVERHEAD_ENTER = -40; + + // The tilt angle above which we conclude that the user would like a rotation + // change to occur and unlock from the overhead state. + private final int TILT_OVERHEAD_EXIT = -15; + // The gap angle in degrees between adjacent orientation angles for hysteresis. // This creates a "dead zone" between the current orientation and a proposed // adjacent orientation. No orientation proposal is made when the orientation @@ -364,12 +390,18 @@ public abstract class WindowOrientationListener { // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). private long mFlatTimestampNanos; + private boolean mFlat; // Timestamp when the device last appeared to be swinging. private long mSwingTimestampNanos; + private boolean mSwinging; // Timestamp when the device last appeared to be undergoing external acceleration. private long mAccelerationTimestampNanos; + private boolean mAccelerating; + + // Whether we are locked into an overhead usage mode. + private boolean mOverhead; // History of observed tilt angles. private static final int TILT_HISTORY_SIZE = 40; @@ -381,6 +413,19 @@ public abstract class WindowOrientationListener { return mProposedRotation; } + public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "mProposedRotation=" + mProposedRotation); + pw.println(prefix + "mPredictedRotation=" + mPredictedRotation); + pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); + pw.println(prefix + "mLastFilteredY=" + mLastFilteredY); + pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ); + pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}"); + pw.println(prefix + "mFlat=" + mFlat); + pw.println(prefix + "mSwinging=" + mSwinging); + pw.println(prefix + "mAccelerating=" + mAccelerating); + pw.println(prefix + "mOverhead=" + mOverhead); + } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @@ -478,7 +523,18 @@ public abstract class WindowOrientationListener { // If the tilt angle is too close to horizontal then we cannot determine // the orientation angle of the screen. - if (Math.abs(tiltAngle) > MAX_TILT) { + if (tiltAngle <= TILT_OVERHEAD_ENTER) { + mOverhead = true; + } else if (tiltAngle >= TILT_OVERHEAD_EXIT) { + mOverhead = false; + } + if (mOverhead) { + if (LOG) { + Slog.v(TAG, "Ignoring sensor data, device is overhead: " + + "tiltAngle=" + tiltAngle); + } + clearPredictedRotationLocked(); + } else if (Math.abs(tiltAngle) > MAX_TILT) { if (LOG) { Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " + "tiltAngle=" + tiltAngle); @@ -526,6 +582,9 @@ public abstract class WindowOrientationListener { } } } + mFlat = isFlat; + mSwinging = isSwinging; + mAccelerating = isAccelerating; // Determine new proposed rotation. oldProposedRotation = mProposedRotation; @@ -543,6 +602,7 @@ public abstract class WindowOrientationListener { + ", isAccelerating=" + isAccelerating + ", isFlat=" + isFlat + ", isSwinging=" + isSwinging + + ", isOverhead=" + mOverhead + ", timeUntilSettledMS=" + remainingMS(now, mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, @@ -660,8 +720,12 @@ public abstract class WindowOrientationListener { mLastFilteredTimestampNanos = Long.MIN_VALUE; mProposedRotation = -1; mFlatTimestampNanos = Long.MIN_VALUE; + mFlat = false; mSwingTimestampNanos = Long.MIN_VALUE; + mSwinging = false; mAccelerationTimestampNanos = Long.MIN_VALUE; + mAccelerating = false; + mOverhead = false; clearPredictedRotationLocked(); clearTiltHistoryLocked(); } @@ -726,6 +790,11 @@ public abstract class WindowOrientationListener { return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; } + private float getLastTiltLocked() { + int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex); + return index >= 0 ? mTiltHistory[index] : Float.NaN; + } + private float remainingMS(long now, long until) { return now >= until ? 0 : (until - now) * 0.000001f; } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java index e5af716..50fe7c7 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java @@ -12,7 +12,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -30,8 +29,8 @@ import com.android.internal.widget.LockPatternUtils; */ public class KeyguardServiceDelegate { // TODO: propagate changes to these to {@link KeyguardTouchDelegate} - public static final String KEYGUARD_PACKAGE = "com.android.keyguard"; - public static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService"; + public static final String KEYGUARD_PACKAGE = "com.android.systemui"; + public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService"; private static final String TAG = "KeyguardServiceDelegate"; private static final boolean DEBUG = true; @@ -45,13 +44,13 @@ public class KeyguardServiceDelegate { // the event something checks before the service is actually started. // KeyguardService itself should default to this state until the real state is known. showing = true; - showingAndNotHidden = true; + showingAndNotOccluded = true; secure = true; } boolean showing; - boolean showingAndNotHidden; + boolean showingAndNotOccluded; boolean inputRestricted; - boolean hidden; + boolean occluded; boolean secure; boolean dreaming; boolean systemIsReady; @@ -103,14 +102,17 @@ public class KeyguardServiceDelegate { }; public KeyguardServiceDelegate(Context context, LockPatternUtils lockPatternUtils) { + mScrim = createScrim(context); + } + + public void bindService(Context context) { Intent intent = new Intent(); intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS); - mScrim = createScrim(context); if (!context.bindServiceAsUser(intent, mKeyguardConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) { if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS); mKeyguardState.showing = false; - mKeyguardState.showingAndNotHidden = false; + mKeyguardState.showingAndNotOccluded = false; mKeyguardState.secure = false; } else { if (DEBUG) Log.v(TAG, "*** Keyguard started"); @@ -149,11 +151,11 @@ public class KeyguardServiceDelegate { return mKeyguardState.showing; } - public boolean isShowingAndNotHidden() { + public boolean isShowingAndNotOccluded() { if (mKeyguardService != null) { - mKeyguardState.showingAndNotHidden = mKeyguardService.isShowingAndNotHidden(); + mKeyguardState.showingAndNotOccluded = mKeyguardService.isShowingAndNotOccluded(); } - return mKeyguardState.showingAndNotHidden; + return mKeyguardState.showingAndNotOccluded; } public boolean isInputRestricted() { @@ -175,11 +177,13 @@ public class KeyguardServiceDelegate { } } - public void setHidden(boolean isHidden) { + public int setOccluded(boolean isOccluded) { + int result = 0; if (mKeyguardService != null) { - mKeyguardService.setHidden(isHidden); + result = mKeyguardService.setOccluded(isOccluded); } - mKeyguardState.hidden = isHidden; + mKeyguardState.occluded = isOccluded; + return result; } public void dismiss() { @@ -249,7 +253,6 @@ public class KeyguardServiceDelegate { if (mKeyguardService != null) { mKeyguardService.onSystemReady(); } else { - if (DEBUG) Log.v(TAG, "onSystemReady() called before keyguard service was ready"); mKeyguardState.systemIsReady = true; } } @@ -273,6 +276,12 @@ public class KeyguardServiceDelegate { mKeyguardState.currentUser = newUserId; } + public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + if (mKeyguardService != null) { + mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration); + } + } + private static final View createScrim(Context context) { View view = new View(context); @@ -288,6 +297,7 @@ public class KeyguardServiceDelegate { stretch, stretch, type, flags, PixelFormat.TRANSLUCENT); lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED; lp.setTitle("KeyguardScrim"); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wm.addView(view, lp); @@ -327,4 +337,9 @@ public class KeyguardServiceDelegate { mKeyguardState.bootCompleted = true; } + public void onActivityDrawn() { + if (mKeyguardService != null) { + mKeyguardService.onActivityDrawn(); + } + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java index 9fb2a50..2778b15 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java @@ -22,6 +22,7 @@ import android.os.RemoteException; import android.util.Slog; import android.view.MotionEvent; +import com.android.internal.policy.IKeyguardServiceConstants; import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; @@ -57,9 +58,9 @@ public class KeyguardServiceWrapper implements IKeyguardService { return false; // TODO cache state } - public boolean isShowingAndNotHidden() { + public boolean isShowingAndNotOccluded() { try { - return mService.isShowingAndNotHidden(); + return mService.isShowingAndNotOccluded(); } catch (RemoteException e) { Slog.w(TAG , "Remote Exception", e); } @@ -100,11 +101,12 @@ public class KeyguardServiceWrapper implements IKeyguardService { } } - public void setHidden(boolean isHidden) { + public int setOccluded(boolean isOccluded) { try { - mService.setHidden(isHidden); + return mService.setOccluded(isOccluded); } catch (RemoteException e) { Slog.w(TAG , "Remote Exception", e); + return IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE; } } @@ -188,6 +190,22 @@ public class KeyguardServiceWrapper implements IKeyguardService { } } + public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + try { + mService.startKeyguardExitAnimation(startTime, fadeoutDuration); + } catch (RemoteException e) { + Slog.w(TAG , "Remote Exception", e); + } + } + + public void onActivityDrawn() { + try { + mService.onActivityDrawn(); + } catch (RemoteException e) { + Slog.w(TAG , "Remote Exception", e); + } + } + public void showAssistant() { // Not used by PhoneWindowManager } |