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

Unverified Commit a8ff075f authored by beanstown106's avatar beanstown106 Committed by Michael Bestas
Browse files

Long-press power while display is off for torch

Long-press the power button while the display is off
to turn the torchlight on and off.

Credits:
 - Lion0738: The main hooks here:
   https://github.com/lion0738/android_frameworks_base/commit/9af2b7844a4d973c8b6c542d7937f56a24a7e5f1


 - Atlantis: The logic on where to hook into this for power button only
 - Alex Cruz: Helping and giving me some pointers along the way

Co-authored-by: default avatarMichael Bestas <mkbestas@lineageos.org>
Co-authored-by: default avatarPranav Vashi <neobuddy89@gmail.com>
Co-authored-by: default avatarSam Mortimer <sam@mortimer.me.uk>
Change-Id: I1aaf8417b865df79e8a1a8d0039b3b860a46be78
parent c7403675
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -849,6 +849,9 @@
    <!-- Used to launch a common app (FlipFlap) for devices with flip cover. -->
    <protected-broadcast android:name="lineageos.intent.action.LID_STATE_CHANGED" />

    <!-- Used for long press power torch feature - automatic turn off on timeout -->
    <protected-broadcast android:name="com.android.server.policy.PhoneWindowManager.ACTION_TORCH_OFF" />

    <!-- ====================================================================== -->
    <!--                          RUNTIME PERMISSIONS                           -->
    <!-- ====================================================================== -->
+1 −0
Original line number Diff line number Diff line
@@ -1148,6 +1148,7 @@
            3 - Power off (without confirmation)
            4 - Go to voice assist
            5 - Go to assistant (Settings.Secure.ASSISTANT)
            6 - Toggle torch on / off (if screen is off)
    -->
    <integer name="config_longPressOnPowerBehavior">5</integer>

+1 −1
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ public class GlobalSettingsValidators {
        VALIDATORS.put(Global.POWER_BUTTON_SHORT_PRESS, new InclusiveIntegerRangeValidator(0, 7));
        VALIDATORS.put(Global.POWER_BUTTON_DOUBLE_PRESS, new InclusiveIntegerRangeValidator(0, 3));
        VALIDATORS.put(Global.POWER_BUTTON_TRIPLE_PRESS, new InclusiveIntegerRangeValidator(0, 3));
        VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 5));
        VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 6));
        VALIDATORS.put(
                Global.POWER_BUTTON_VERY_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 1));
        VALIDATORS.put(Global.KEY_CHORD_POWER_VOLUME_UP, new InclusiveIntegerRangeValidator(0, 2));
+157 −3
Original line number Diff line number Diff line
@@ -106,10 +106,12 @@ import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUiModeManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.app.UiModeManager;
@@ -130,6 +132,9 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.SensorPrivacyManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
@@ -248,6 +253,8 @@ import com.android.server.wm.WindowManagerInternal.AppTransitionListener;

import dalvik.system.PathClassLoader;

import lineageos.providers.LineageSettings;

import org.lineageos.internal.buttons.LineageButtons;

import java.io.File;
@@ -307,6 +314,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
    static final int LONG_PRESS_POWER_GO_TO_VOICE_ASSIST = 4;
    static final int LONG_PRESS_POWER_ASSISTANT = 5; // Settings.Secure.ASSISTANT
    static final int LONG_PRESS_POWER_TORCH = 6;

    // must match: config_veryLongPresOnPowerBehavior in config.xml
    // The config value can be overridden using Settings.Global.POWER_BUTTON_VERY_LONG_PRESS
@@ -393,6 +401,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;

    private static final String ACTION_TORCH_OFF =
            "com.android.server.policy.PhoneWindowManager.ACTION_TORCH_OFF";

    /**
     * Keyguard stuff
     */
@@ -462,6 +473,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    AccessibilityManagerInternal mAccessibilityManagerInternal;
    BurnInProtectionHelper mBurnInProtectionHelper;
    private DisplayFoldController mDisplayFoldController;
    AlarmManager mAlarmManager;
    AppOpsManager mAppOpsManager;
    PackageManager mPackageManager;
    SideFpsEventHandler mSideFpsEventHandler;
@@ -670,6 +682,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    // behavior will be skipped if the default display is non-interactive.
    private boolean mSupportShortPressPowerWhenDefaultDisplayOn;

    // Power long press action saved on key down that should happen on key up
    private int mResolvedLongPressOnPowerBehavior;

    // Whether to go to sleep entering theater mode from power button
    private boolean mGoToSleepOnButtonPressTheaterMode;

@@ -750,6 +765,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26;
    private static final int MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE = 27;

    // Lineage additions
    private static final int MSG_TOGGLE_TORCH = 100;

    private CameraManager mCameraManager;
    private String mRearFlashCameraId;
    private boolean mTorchLongPressPowerEnabled;
    private boolean mTorchEnabled;
    private int mTorchTimeout;
    private PendingIntent mTorchOffPendingIntent;

    private class PolicyHandler extends Handler {

        private PolicyHandler(Looper looper) {
@@ -843,6 +868,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    final long downTime = (Long) msg.obj;
                    mDeferredKeyActionExecutor.setActionsExecutable(keyCode, downTime);
                    break;
                case MSG_TOGGLE_TORCH:
                    toggleTorch();
                    break;
            }
        }
    }
@@ -928,6 +956,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(LineageSettings.System.getUriFor(
                    LineageSettings.System.TORCH_LONG_PRESS_POWER_GESTURE), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(LineageSettings.System.getUriFor(
                    LineageSettings.System.TORCH_LONG_PRESS_POWER_TIMEOUT), false, this,
                    UserHandle.USER_ALL);
            updateSettings();
        }

@@ -1082,9 +1116,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mPowerKeyHandled = mPowerKeyHandled || hungUp
                || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
        if (!mPowerKeyHandled) {
            mResolvedLongPressOnPowerBehavior = getResolvedLongPressOnPowerBehavior();
            if (!interactive) {
                if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                    wakeUpFromWakeKey(event);
                } else if (mSupportLongPressPowerWhenNonInteractive &&
                        hasLongPressOnPowerBehavior()) {
                    if (mResolvedLongPressOnPowerBehavior != LONG_PRESS_POWER_TORCH) {
                        wakeUpFromWakeKey(event);
                    }
                }
            }
        } else {
            // handled by another power key policy.
            if (mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
@@ -1104,6 +1146,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
                // Abort possibly stuck animations only when power key up without long press case.
                mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
                // See if we deferred screen wake because long press power for torch is enabled
                if (mResolvedLongPressOnPowerBehavior == LONG_PRESS_POWER_TORCH && !isScreenOn()) {
                    wakeUpFromWakeKey(event);
                }
            }
        }

@@ -1406,9 +1452,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    }

    private void powerLongPress(long eventTime) {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        final int behavior = mResolvedLongPressOnPowerBehavior;
        Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
                + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);
                + " mResolvedLongPressOnPowerBehavior=" + mResolvedLongPressOnPowerBehavior);

        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
@@ -1449,6 +1495,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                launchAssistAction(null, powerKeyDeviceId, eventTime,
                        AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
                break;
            case LONG_PRESS_POWER_TORCH:
                mPowerKeyHandled = true;
                // Toggle torch state asynchronously to help protect against
                // a misbehaving cameraservice from blocking systemui.
                mHandler.removeMessages(MSG_TOGGLE_TORCH);
                Message msg = mHandler.obtainMessage(MSG_TOGGLE_TORCH);
                msg.setAsynchronous(true);
                msg.sendToTarget();
                break;
        }
    }

@@ -1515,6 +1570,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (FactoryTest.isLongPressOnPowerOffEnabled()) {
            return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
        }
        if (mTorchLongPressPowerEnabled && (!isScreenOn() || mTorchEnabled)) {
            return LONG_PRESS_POWER_TORCH;
        }

        // If the config indicates the assistant behavior but the device isn't yet provisioned, show
        // global actions instead.
@@ -2296,6 +2354,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
        mSettingsObserver = new SettingsObserver(mHandler);
        mSettingsObserver.observe();

        // Lineage additions
        mAlarmManager = mContext.getSystemService(AlarmManager.class);
        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        mCameraManager.registerTorchCallback(new TorchModeCallback(), mHandler);

        mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
        mUiMode = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultUiModeType);
@@ -2487,6 +2551,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (DEBUG_INPUT) {
            Slog.d(TAG, "" + mDeviceKeyHandlers.size() + " device key handlers loaded");
        }

        // Register for torch off events
        BroadcastReceiver torchReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                mTorchOffPendingIntent = null;
                if (mTorchEnabled) {
                    mHandler.removeMessages(MSG_TOGGLE_TORCH);
                    Message msg = mHandler.obtainMessage(MSG_TOGGLE_TORCH);
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                }
            }
        };
        filter = new IntentFilter();
        filter.addAction(ACTION_TORCH_OFF);
        mContext.registerReceiver(torchReceiver, filter);
    }

    private void initKeyCombinationRules() {
@@ -2954,6 +3035,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                mRingerToggleChord = VOLUME_HUSH_OFF;
            }

            mTorchLongPressPowerEnabled = LineageSettings.System.getIntForUser(
                    resolver, LineageSettings.System.TORCH_LONG_PRESS_POWER_GESTURE, 0,
                    UserHandle.USER_CURRENT) == 1;
            mTorchTimeout = LineageSettings.System.getIntForUser(
                    resolver, LineageSettings.System.TORCH_LONG_PRESS_POWER_TIMEOUT, 0,
                    UserHandle.USER_CURRENT);

            // Configure wake gesture.
            boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.WAKE_GESTURE_ENABLED, 0,
@@ -6766,6 +6854,70 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
    }

    private void cancelTorchOff() {
        if (mTorchOffPendingIntent != null) {
            mAlarmManager.cancel(mTorchOffPendingIntent);
            mTorchOffPendingIntent = null;
        }
    }

    private void toggleTorch() {
        cancelTorchOff();
        final boolean origEnabled = mTorchEnabled;
        try {
            final String rearFlashCameraId = getRearFlashCameraId();
            if (rearFlashCameraId != null) {
                mCameraManager.setTorchMode(rearFlashCameraId, !mTorchEnabled);
                mTorchEnabled = !mTorchEnabled;
            }
        } catch (CameraAccessException e) {
            // Ignore
        }
        // Setup torch off alarm
        if (mTorchEnabled && !origEnabled && mTorchTimeout > 0) {
            Intent torchOff = new Intent(ACTION_TORCH_OFF);
            torchOff.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                    | Intent.FLAG_RECEIVER_FOREGROUND);
            mTorchOffPendingIntent = PendingIntent.getBroadcast(mContext, 0, torchOff,
                    PendingIntent.FLAG_IMMUTABLE);
            mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    SystemClock.elapsedRealtime() + mTorchTimeout * 1000, mTorchOffPendingIntent);
        }
    }

    private String getRearFlashCameraId() throws CameraAccessException {
        if (mRearFlashCameraId != null) return mRearFlashCameraId;
        for (final String id : mCameraManager.getCameraIdList()) {
            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
            if (flashAvailable != null && flashAvailable
                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                mRearFlashCameraId = id;
                break;
            }
        }
        return mRearFlashCameraId;
    }

    private class TorchModeCallback extends CameraManager.TorchCallback {
        @Override
        public void onTorchModeChanged(String cameraId, boolean enabled) {
            if (!cameraId.equals(mRearFlashCameraId)) return;
            mTorchEnabled = enabled;
            if (!mTorchEnabled) {
                cancelTorchOff();
            }
        }

        @Override
        public void onTorchModeUnavailable(String cameraId) {
            if (!cameraId.equals(mRearFlashCameraId)) return;
            mTorchEnabled = false;
            cancelTorchOff();
        }
    }

    private static String endcallBehaviorToString(int behavior) {
        StringBuilder sb = new StringBuilder();
        if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
@@ -6872,6 +7024,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                return "LONG_PRESS_POWER_GO_TO_VOICE_ASSIST";
            case LONG_PRESS_POWER_ASSISTANT:
                return "LONG_PRESS_POWER_ASSISTANT";
            case LONG_PRESS_POWER_TORCH:
                return "LONG_PRESS_POWER_TORCH";
            default:
                return Integer.toString(behavior);
        }