Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9b70bdc authored by John Spurlock's avatar John Spurlock
Browse files

Simplify immersive mode confirmation cling logic.

Instead of keeping track of confirmations per-package, track
a global confirmation per-user.  If the panic signal is received,
reshow the cling at most once per-user per-reboot.

Ensure the nav bar becomes visible after the panic signal.
Usually this happens as a side effect of showing the keyguard.
However, in the case where there is no keyguard (Security = None)
show the transient nav bar temporarily as a hint.

Also listen to the correct observer uri to pick up confirmation
setting changes.

Bug:12242125
Change-Id: Ic95e2a8630ec3802b8ef462fcaa92366b9343a3f
parent 3ff18faa
Loading
Loading
Loading
Loading
+68 −59
Original line number Diff line number Diff line
@@ -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;
@@ -86,82 +85,91 @@ public class ImmersiveModeConfirmation {
    }

    public void loadSetting() {
        if (DEBUG) Slog.d(TAG, "loadSetting()");
        mConfirmedPackages.clear();
        String packages = null;
        mConfirmed = false;
        mCurrentUserId = getCurrentUser();
        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 || PolicyControl.disableImmersiveConfirmation(pkg)) {
            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);
            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)) {
                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);
    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 int getCurrentUser() {
        try {
            return ActivityManagerNative.getDefault().getCurrentUser().id;
        } catch (RemoteException e) {
            throw new IllegalStateException(e); // local call
        }
    }

    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;
        }
@@ -297,11 +305,10 @@ public class ImmersiveModeConfirmation {
        }
    }

    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 +320,35 @@ public class ImmersiveModeConfirmation {
        mWindowManager.addView(mClingWindow, lp);
    }

    private Runnable confirmAction(final String pkg) {
        return new Runnable() {
    private final Runnable mConfirm = 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);
            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;
            }
        }
    }
+13 −3
Original line number Diff line number Diff line
@@ -523,7 +523,7 @@ 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(
@@ -3950,8 +3950,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                if (down) {
                    mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn, event.getDownTime(),
                            isImmersiveMode(mLastSystemUiFlags));
                    boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
                            event.getDownTime(), isImmersiveMode(mLastSystemUiFlags));
                    if (panic) {
                        mHandler.post(mRequestTransientNav);
                    }
                    if (isScreenOn && !mPowerKeyTriggered
                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                        mPowerKeyTriggered = true;
@@ -4218,6 +4221,13 @@ 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()) {
            boolean sb = mStatusBarController.checkShowTransientBarLw();