From f1a36648ca099d7ffa8a19e403673820d4b8417a Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Sat, 12 Oct 2013 17:50:42 -0400 Subject: Implement new SYSTEM_UI_FLAG_IMMERSIVE_STICKY. Migrate transient bar mode to IMMERSIVE_STICKY, and introduce new behavior for IMMERSIVE: namely the opaque bars are revealed by clearing the flags on swipe. Remove low-profile optimization that confuses api demos and other apps using low-profile as a signal. TransientNavigationConfirmation renamed to ImmersiveModeConfirmation, and its associated resources, since the confirmation is now shown when the nav bar is shown in either of the two immersive modes. Remove unused Toast.makeBar and associated hidden framework bits now that the confirmation uses a cling instead. Bug:11062108 Change-Id: Iae49d31973940b9bee9f5b1827756db5eaa76aa3 --- .../policy/impl/ImmersiveModeConfirmation.java | 338 ++++++++++++++++++++ .../internal/policy/impl/PhoneWindowManager.java | 78 +++-- .../impl/TransientNavigationConfirmation.java | 339 --------------------- 3 files changed, 384 insertions(+), 371 deletions(-) create mode 100644 policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java delete mode 100644 policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java (limited to 'policy/src') diff --git a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java new file mode 100644 index 0000000..dfdcdad5 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2013 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.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.os.Handler; +import android.os.Message; +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.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.Button; +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. + */ +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 final Context mContext; + private final H mHandler; + private final ArraySet mConfirmedPackages = new ArraySet(); + private final long mShowDelayMs; + private final long mPanicThresholdMs; + + private ClingWindowView mClingWindow; + private String mLastPackage; + private String mPromptPackage; + private long mPanicTime; + private String mPanicPackage; + private WindowManager mWindowManager; + + public ImmersiveModeConfirmation(Context context) { + mContext = context; + mHandler = new H(); + mShowDelayMs = getNavBarExitDuration() * 3; + mPanicThresholdMs = context.getResources() + .getInteger(R.integer.config_immersive_mode_confirmation_panic); + mWindowManager = (WindowManager) + mContext.getSystemService(Context.WINDOW_SERVICE); + } + + private long getNavBarExitDuration() { + Animation exit = AnimationUtils.loadAnimation(mContext, R.anim.dock_bottom_exit); + return exit != null ? exit.getDuration() : 0; + } + + public void loadSetting() { + if (DEBUG) Slog.d(TAG, "loadSetting()"); + mConfirmedPackages.clear(); + String packages = null; + try { + packages = 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); + } + } catch (Throwable t) { + Slog.w(TAG, "Error loading confirmations, packages=" + packages, t); + } + } + + private void saveSetting() { + if (DEBUG) Slog.d(TAG, "saveSetting()"); + try { + final String packages = TextUtils.join(",", mConfirmedPackages); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, + packages, + UserHandle.USER_CURRENT); + if (DEBUG) Slog.d(TAG, "Saved packages=" + packages); + } catch (Throwable t) { + Slog.w(TAG, "Error saving confirmations, mConfirmedPackages=" + mConfirmedPackages, t); + } + } + + public void immersiveModeChanged(String pkg, boolean isImmersiveMode) { + if (pkg == null) { + return; + } + mHandler.removeMessages(H.SHOW); + if (isImmersiveMode) { + mLastPackage = pkg; + if (DEBUG_SHOW_EVERY_TIME || !mConfirmedPackages.contains(pkg)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(H.SHOW, pkg), mShowDelayMs); + } + } else { + mLastPackage = null; + mHandler.sendEmptyMessage(H.HIDE); + } + } + + public void onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode) { + if (mPanicPackage != null && !isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { + // turning the screen back on within the panic threshold + unconfirmPackage(mPanicPackage); + } + if (isScreenOn && inImmersiveMode) { + // turning the screen off, remember if we were in immersive mode + mPanicTime = time; + mPanicPackage = mLastPackage; + } else { + mPanicTime = 0; + mPanicPackage = null; + } + } + + public void confirmCurrentPrompt() { + mHandler.post(confirmAction(mPromptPackage)); + } + + 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 handleHide() { + if (mClingWindow != null) { + if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation for " + mPromptPackage); + mWindowManager.removeView(mClingWindow); + mClingWindow = null; + } + } + + public WindowManager.LayoutParams getClingWindowLayoutParams() { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_TOAST, + 0 + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + , + PixelFormat.TRANSLUCENT); + lp.setTitle("ImmersiveModeConfirmation"); + lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; + lp.gravity = Gravity.FILL; + return lp; + } + + public FrameLayout.LayoutParams getBubbleLayoutParams() { + return new FrameLayout.LayoutParams( + mContext.getResources().getDimensionPixelSize( + R.dimen.immersive_mode_cling_width), + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_HORIZONTAL | Gravity.TOP); + } + + private class ClingWindowView extends FrameLayout { + private static final int BGCOLOR = 0x80000000; + private static final int OFFSET_DP = 48; + + private final Runnable mConfirm; + private final ColorDrawable mColor = new ColorDrawable(0); + private ValueAnimator mColorAnim; + private ViewGroup mClingLayout; + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + if (mClingLayout != null && mClingLayout.getParent() != null) { + mClingLayout.setLayoutParams(getBubbleLayoutParams()); + } + } + } + }; + + public ClingWindowView(Context context, Runnable confirm) { + super(context); + mConfirm = confirm; + setClickable(true); + setBackground(mColor); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + DisplayMetrics metrics = new DisplayMetrics(); + mWindowManager.getDefaultDisplay().getMetrics(metrics); + float density = metrics.density; + + // create the confirmation cling + mClingLayout = (ViewGroup) + View.inflate(getContext(), R.layout.immersive_mode_cling, null); + + final Button ok = (Button) mClingLayout.findViewById(R.id.ok); + ok.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mConfirm.run(); + } + }); + addView(mClingLayout, getBubbleLayoutParams()); + + if (ActivityManager.isHighEndGfx()) { + final View bubble = mClingLayout.findViewById(R.id.text); + bubble.setAlpha(0f); + bubble.setTranslationY(-OFFSET_DP*density); + bubble.animate() + .alpha(1f) + .translationY(0) + .setDuration(300) + .setInterpolator(new DecelerateInterpolator()) + .start(); + + ok.setAlpha(0f); + ok.setTranslationY(-OFFSET_DP*density); + ok.animate().alpha(1f) + .translationY(0) + .setDuration(300) + .setStartDelay(200) + .setInterpolator(new DecelerateInterpolator()) + .start(); + + mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, BGCOLOR); + mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final int c = (Integer) animation.getAnimatedValue(); + mColor.setColor(c); + } + }); + mColorAnim.setDuration(1000); + mColorAnim.start(); + } else { + mColor.setColor(BGCOLOR); + } + + mContext.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); + } + + @Override + public void onDetachedFromWindow() { + mContext.unregisterReceiver(mReceiver); + } + + @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); + + mClingWindow = new ClingWindowView(mContext, confirmAction(pkg)); + + // we will be hiding the nav bar, so layout as if it's already hidden + mClingWindow.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + + // show the confirmation + WindowManager.LayoutParams lp = getClingWindowLayoutParams(); + 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 class H extends Handler { + private static final int SHOW = 0; + private static final int HIDE = 1; + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case SHOW: + handleShow((String)msg.obj); + break; + case HIDE: + handleHide(); + break; + } + } + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 6dcaddc..5ac3ed0 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -532,7 +532,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( - Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS), false, this, + Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this, UserHandle.USER_ALL); updateSettings(); } @@ -570,7 +570,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { StatusBarManager.WINDOW_NAVIGATION_BAR, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - private TransientNavigationConfirmation mTransientNavigationConfirmation; + private ImmersiveModeConfirmation mImmersiveModeConfirmation; private SystemGesturesPointerEventListener mSystemGestures; @@ -953,7 +953,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // no-op } }); - mTransientNavigationConfirmation = new TransientNavigationConfirmation(mContext); + mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext); mWindowManagerFuncs.registerPointerEventListener(mSystemGestures); mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); @@ -1169,8 +1169,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHasSoftInput = hasSoftInput; updateRotation = true; } - if (mTransientNavigationConfirmation != null) { - mTransientNavigationConfirmation.loadSetting(); + if (mImmersiveModeConfirmation != null) { + mImmersiveModeConfirmation.loadSetting(); } } if (updateRotation) { @@ -2699,15 +2699,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int sysui = mLastSystemUiFlags; boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; boolean navTranslucent = (sysui & View.NAVIGATION_BAR_TRANSLUCENT) != 0; - boolean transientAllowed = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; - navTranslucent &= !transientAllowed; // transient trumps translucent + 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 &= isTranslucentNavigationAllowed(); // When the navigation bar isn't visible, we put up a fake // input window to catch all touch events. This way we can // detect when the user presses anywhere to bring back the nav // bar and ensure the application doesn't see the event. - if (navVisible || transientAllowed) { + if (navVisible || navAllowedHidden) { if (mHideNavFakeWindow != null) { mHideNavFakeWindow.dismiss(); mHideNavFakeWindow = null; @@ -3945,8 +3947,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { - mTransientNavigationConfirmation.onPowerKeyDown(isScreenOn, event.getDownTime(), - isTransientNavigationAllowed(mLastSystemUiFlags)); + mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn, event.getDownTime(), + isImmersiveMode(mLastSystemUiFlags)); if (isScreenOn && !mPowerKeyTriggered && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { mPowerKeyTriggered = true; @@ -4225,7 +4227,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (sb) mStatusBarController.showTransient(); if (nb) mNavigationBarController.showTransient(); - mTransientNavigationConfirmation.confirmCurrentPrompt(); + mImmersiveModeConfirmation.confirmCurrentPrompt(); updateSystemUiVisibilityLw(); } } @@ -5108,6 +5110,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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; vis = (vis & ~flags) | (oldVis & flags); @@ -5118,53 +5121,64 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // update status bar - boolean transientAllowed = - (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; + boolean immersiveSticky = + (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean hideStatusBarWM = mTopFullscreenOpaqueWindowState != null && (mTopFullscreenOpaqueWindowState.getAttrs().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; boolean hideStatusBarSysui = (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; + boolean hideNavBarSysui = + (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; boolean transientStatusBarAllowed = mStatusBar != null && ( hideStatusBarWM - || (hideStatusBarSysui && transientAllowed) + || (hideStatusBarSysui && immersiveSticky) || statusBarHasFocus); - if (mStatusBarController.isTransientShowing() - && !transientStatusBarAllowed && hideStatusBarSysui) { + boolean transientNavBarAllowed = + mNavigationBar != null && + hideNavBarSysui && immersiveSticky; + + boolean denyTransientStatus = mStatusBarController.isTransientShowing() + && !transientStatusBarAllowed && hideStatusBarSysui; + boolean denyTransientNav = mNavigationBarController.isTransientShowing() + && !transientNavBarAllowed; + if (denyTransientStatus || denyTransientNav) { // clear the clearable flags instead - int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; - if (newVal != mResettingSystemUiFlags) { - mResettingSystemUiFlags = newVal; - mWindowManagerFuncs.reevaluateStatusBarVisibility(); - } + clearClearableFlagsLw(); } vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); // update navigation bar - boolean oldTransientNav = isTransientNavigationAllowed(oldVis); - boolean isTransientNav = isTransientNavigationAllowed(vis); - if (win != null && oldTransientNav != isTransientNav) { + boolean oldImmersiveMode = isImmersiveMode(oldVis); + boolean newImmersiveMode = isImmersiveMode(vis); + if (win != null && oldImmersiveMode != newImmersiveMode) { final String pkg = win.getOwningPackage(); - mTransientNavigationConfirmation.transientNavigationChanged(pkg, isTransientNav); + mImmersiveModeConfirmation.immersiveModeChanged(pkg, newImmersiveMode); } - vis = mNavigationBarController.updateVisibilityLw(isTransientNav, oldVis, vis); - // don't send low profile updates if the system bars are hidden - if (mStatusBarController.isHidden() && mNavigationBarController.isHidden()) { - vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; - } + vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); + return vis; } - private boolean isTransientNavigationAllowed(int vis) { + private void clearClearableFlagsLw() { + int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; + if (newVal != mResettingSystemUiFlags) { + mResettingSystemUiFlags = newVal; + mWindowManagerFuncs.reevaluateStatusBarVisibility(); + } + } + + private boolean isImmersiveMode(int vis) { + final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; return mNavigationBar != null && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 - && (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; + && (vis & flags) != 0; } /** diff --git a/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java b/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java deleted file mode 100644 index 3c7902c..0000000 --- a/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2013 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.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.app.ActivityManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; -import android.os.Handler; -import android.os.Message; -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.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.view.animation.DecelerateInterpolator; -import android.widget.Button; -import android.widget.FrameLayout; - -import com.android.internal.R; - -import java.util.Arrays; - -/** - * Helper to manage showing/hiding a confirmation prompt when the transient navigation bar - * is hidden. - */ -public class TransientNavigationConfirmation { - private static final String TAG = "TransientNavigationConfirmation"; - private static final boolean DEBUG = false; - private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution - - private final Context mContext; - private final H mHandler; - private final ArraySet mConfirmedPackages = new ArraySet(); - private final long mShowDelayMs; - private final long mPanicThresholdMs; - - private ClingWindowView mClingWindow; - private String mLastPackage; - private String mPromptPackage; - private long mPanicTime; - private String mPanicPackage; - private WindowManager mWindowManager; - - public TransientNavigationConfirmation(Context context) { - mContext = context; - mHandler = new H(); - mShowDelayMs = getNavBarExitDuration() * 3; - mPanicThresholdMs = context.getResources() - .getInteger(R.integer.config_transient_navigation_confirmation_panic); - mWindowManager = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); - } - - private long getNavBarExitDuration() { - Animation exit = AnimationUtils.loadAnimation(mContext, R.anim.dock_bottom_exit); - return exit != null ? exit.getDuration() : 0; - } - - public void loadSetting() { - if (DEBUG) Slog.d(TAG, "loadSetting()"); - mConfirmedPackages.clear(); - String packages = null; - try { - packages = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS, - UserHandle.USER_CURRENT); - if (packages != null) { - mConfirmedPackages.addAll(Arrays.asList(packages.split(","))); - if (DEBUG) Slog.d(TAG, "Loaded mConfirmedPackages=" + mConfirmedPackages); - } - } catch (Throwable t) { - Slog.w(TAG, "Error loading confirmations, packages=" + packages, t); - } - } - - private void saveSetting() { - if (DEBUG) Slog.d(TAG, "saveSetting()"); - try { - final String packages = TextUtils.join(",", mConfirmedPackages); - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS, - packages, - UserHandle.USER_CURRENT); - if (DEBUG) Slog.d(TAG, "Saved packages=" + packages); - } catch (Throwable t) { - Slog.w(TAG, "Error saving confirmations, mConfirmedPackages=" + mConfirmedPackages, t); - } - } - - public void transientNavigationChanged(String pkg, boolean isNavTransient) { - if (pkg == null) { - return; - } - mHandler.removeMessages(H.SHOW); - if (isNavTransient) { - mLastPackage = pkg; - if (DEBUG_SHOW_EVERY_TIME || !mConfirmedPackages.contains(pkg)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(H.SHOW, pkg), mShowDelayMs); - } - } else { - mLastPackage = null; - mHandler.sendEmptyMessage(H.HIDE); - } - } - - public void onPowerKeyDown(boolean isScreenOn, long time, boolean transientNavigationAllowed) { - if (mPanicPackage != null && !isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { - // turning the screen back on within the panic threshold - unconfirmPackage(mPanicPackage); - } - if (isScreenOn && transientNavigationAllowed) { - // turning the screen off, remember if we were hiding the transient nav - mPanicTime = time; - mPanicPackage = mLastPackage; - } else { - mPanicTime = 0; - mPanicPackage = null; - } - } - - public void confirmCurrentPrompt() { - mHandler.post(confirmAction(mPromptPackage)); - } - - private void unconfirmPackage(String pkg) { - if (pkg != null) { - if (DEBUG) Slog.d(TAG, "Unconfirming transient navigation for " + pkg); - mConfirmedPackages.remove(pkg); - saveSetting(); - } - } - - private void handleHide() { - if (mClingWindow != null) { - if (DEBUG) Slog.d(TAG, - "Hiding transient navigation confirmation for " + mPromptPackage); - mWindowManager.removeView(mClingWindow); - mClingWindow = null; - } - } - - public WindowManager.LayoutParams getClingWindowLayoutParams() { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_TOAST, - 0 - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - , - PixelFormat.TRANSLUCENT); - lp.setTitle("TransientNavigationConfirmation"); - lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; - lp.gravity = Gravity.FILL; - return lp; - } - - public FrameLayout.LayoutParams getBubbleLayoutParams() { - return new FrameLayout.LayoutParams( - mContext.getResources().getDimensionPixelSize( - R.dimen.immersive_mode_cling_width), - ViewGroup.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_HORIZONTAL | Gravity.TOP); - } - - private class ClingWindowView extends FrameLayout { - private static final int BGCOLOR = 0x80000000; - private static final int OFFSET_DP = 48; - - private final Runnable mConfirm; - private final ColorDrawable mColor = new ColorDrawable(0); - private ValueAnimator mColorAnim; - private ViewGroup mClingLayout; - - private BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) { - if (mClingLayout != null && mClingLayout.getParent() != null) { - mClingLayout.setLayoutParams(getBubbleLayoutParams()); - } - } - } - }; - - public ClingWindowView(Context context, Runnable confirm) { - super(context); - mConfirm = confirm; - setClickable(true); - setBackground(mColor); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - DisplayMetrics metrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(metrics); - float density = metrics.density; - - // create the confirmation cling - mClingLayout = (ViewGroup) - View.inflate(getContext(), R.layout.transient_navigation_cling, null); - - final Button ok = (Button) mClingLayout.findViewById(R.id.ok); - ok.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mConfirm.run(); - } - }); - addView(mClingLayout, getBubbleLayoutParams()); - - if (ActivityManager.isHighEndGfx()) { - final View bubble = mClingLayout.findViewById(R.id.text); - bubble.setAlpha(0f); - bubble.setTranslationY(-OFFSET_DP*density); - bubble.animate() - .alpha(1f) - .translationY(0) - .setDuration(300) - .setInterpolator(new DecelerateInterpolator()) - .start(); - - ok.setAlpha(0f); - ok.setTranslationY(-OFFSET_DP*density); - ok.animate().alpha(1f) - .translationY(0) - .setDuration(300) - .setStartDelay(200) - .setInterpolator(new DecelerateInterpolator()) - .start(); - - mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, BGCOLOR); - mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final int c = (Integer) animation.getAnimatedValue(); - mColor.setColor(c); - } - }); - mColorAnim.setDuration(1000); - mColorAnim.start(); - } else { - mColor.setColor(BGCOLOR); - } - - mContext.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); - } - - @Override - public void onDetachedFromWindow() { - mContext.unregisterReceiver(mReceiver); - } - - @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 transient navigation confirmation for " + pkg); - - mClingWindow = new ClingWindowView(mContext, confirmAction(pkg)); - - // we will be hiding the nav bar, so layout as if it's already hidden - mClingWindow.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - - // show the confirmation - WindowManager.LayoutParams lp = getClingWindowLayoutParams(); - 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 transient navigation for " + pkg); - mConfirmedPackages.add(pkg); - saveSetting(); - } - handleHide(); - } - }; - } - - private final class H extends Handler { - private static final int SHOW = 0; - private static final int HIDE = 1; - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case SHOW: - handleShow((String)msg.obj); - break; - case HIDE: - handleHide(); - break; - } - } - } -} -- cgit v1.1