diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fe3e09f032b19f603b3b7d4688fa3a53d5408646..d645927a1244512985d65a1f621a7cff02a073a3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5467,6 +5467,25 @@ public final class Settings { */ public static final String VOLBTN_MUSIC_CONTROLS = "volbtn_music_controls"; + /** + * Activate torchlight when power button is + * long-pressed while the display is off + * The value is boolean (1 or 0). + * @hide + */ + public static final String TORCH_LONG_PRESS_POWER_GESTURE = + "torch_long_press_power_gesture"; + + /** + * When the torch has been turned on by long press on power, + * automatically turn off after a configurable number of seconds. + * The value is an integer number of seconds in the range 0-3600. + * 0 means never automatically turn off. + * @hide + */ + public static final String TORCH_LONG_PRESS_POWER_TIMEOUT = + "torch_long_press_power_timeout"; + /** * IMPORTANT: If you add a new public settings you also have to add it to * PUBLIC_SETTINGS below. If the new setting is hidden you have to add diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f8b10c441a20263ea1a12292eb43e37ffb71c110..d8e2e5e8e93fb3c40c7fa178432c8672c40fa317 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -708,6 +708,11 @@ <protected-broadcast android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" /> <protected-broadcast android:name="android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER" /> + <!-- LMODroid additions --> + + <!-- 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 --> <!-- ====================================================================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d9007e50c7c664e5de1d325809d1d3182166cc44..7305ccc93e938dd727a1f6d24a228aa7d2814b2c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -976,6 +976,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> diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index d9dbc46db24d8c2e88ba53f444c779616ea9fbc2..77ff821bfd1537cb5fad30c3d5c04e18cd24f981 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -126,7 +126,7 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.REQUIRE_PASSWORD_TO_DECRYPT, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.AWARE_ALLOWED, BOOLEAN_VALIDATOR); - 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)); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index a61884639a0097b9735fce6892b3a82390d21fb4..7379550d62deb88343de695a515a1cebe87a93ea 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -97,9 +97,11 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; +import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IUiModeManager; import android.app.NotificationManager; +import android.app.PendingIntent; import android.app.ProgressDialog; import android.app.SearchManager; import android.app.UiModeManager; @@ -120,6 +122,9 @@ import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; +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; @@ -278,6 +283,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 static final int VERY_LONG_PRESS_POWER_NOTHING = 0; @@ -329,6 +335,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); + private static final String ACTION_TORCH_OFF = + "com.android.server.policy.PhoneWindowManager.ACTION_TORCH_OFF"; + /** * Keyguard stuff */ @@ -387,6 +396,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { AccessibilityManager mAccessibilityManager; BurnInProtectionHelper mBurnInProtectionHelper; private DisplayFoldController mDisplayFoldController; + AlarmManager mAlarmManager; AppOpsManager mAppOpsManager; PackageManager mPackageManager; SideFpsEventHandler mSideFpsEventHandler; @@ -564,6 +574,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Whether to support long press from power button in non-interactive mode private boolean mSupportLongPressPowerWhenNonInteractive; + // 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; @@ -633,6 +646,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; + // LMODroid 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 LineageHardwareManager mLineageHardware; private class PolicyHandler extends Handler { @@ -701,6 +724,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_RINGER_TOGGLE_CHORD: handleRingerChordGesture(); break; + case MSG_TOGGLE_TORCH: + toggleTorch(); + break; } } } @@ -759,6 +785,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.TORCH_LONG_PRESS_POWER_GESTURE), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.TORCH_LONG_PRESS_POWER_TIMEOUT), false, this, + UserHandle.USER_ALL); if (mLineageHardware.isSupported(LineageHardwareManager.FEATURE_KEY_SWAP)) { resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SWAP_CAPACITIVE_KEYS), false, this, @@ -907,8 +939,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyHandled = mPowerKeyHandled || hungUp || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { + mResolvedLongPressOnPowerBehavior = getResolvedLongPressOnPowerBehavior(); if (!interactive) { - wakeUpFromPowerKey(event.getDownTime()); + if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { + wakeUpFromPowerKey(event.getDownTime()); + } else if (mSupportLongPressPowerWhenNonInteractive && + hasLongPressOnPowerBehavior()) { + if (mResolvedLongPressOnPowerBehavior != LONG_PRESS_POWER_TORCH) { + wakeUpFromPowerKey(event.getDownTime()); + } + } } } else { // handled by another power key policy. @@ -925,6 +965,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()) { + wakeUpFromPowerKey(SystemClock.uptimeMillis()); + } } } else { // handled by single key or another power key policy. @@ -1109,9 +1153,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: @@ -1147,6 +1191,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; } } @@ -1204,6 +1257,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (FactoryTest.isLongPressOnPowerOffEnabled()) { return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; } + if (mTorchLongPressPowerEnabled && !isScreenOn()) { + return LONG_PRESS_POWER_TORCH; + } // If the config indicates the assistant behavior but the device isn't yet provisioned, show // global actions instead. @@ -1698,6 +1754,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHandler = new PolicyHandler(); mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); mSettingsObserver = new SettingsObserver(mHandler); + + // Kaleidoscope additions + mAlarmManager = mContext.getSystemService(AlarmManager.class); + mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + mCameraManager.registerTorchCallback(new TorchModeCallback(), mHandler); + mModifierShortcutManager = new ModifierShortcutManager(context); mUiMode = context.getResources().getInteger( com.android.internal.R.integer.config_defaultUiModeType); @@ -1898,6 +1960,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); + context.registerReceiver(torchReceiver, filter); } private void initKeyCombinationRules() { @@ -2163,6 +2242,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF; } + mTorchLongPressPowerEnabled = Settings.System.getIntForUser( + resolver, Settings.System.TORCH_LONG_PRESS_POWER_GESTURE, 0, + UserHandle.USER_CURRENT) == 1; + mTorchTimeout = Settings.System.getIntForUser( + resolver, Settings.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, @@ -5527,6 +5613,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 ) { @@ -5631,6 +5781,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); }