diff --git a/parts/AndroidManifest.xml b/parts/AndroidManifest.xml
index 3dc43ff..29a6a39 100644
--- a/parts/AndroidManifest.xml
+++ b/parts/AndroidManifest.xml
@@ -113,6 +113,64 @@
android:icon="@drawable/ic_thermal"
android:label="@string/thermal_title"
android:exported="true"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_custom_seekbar_plus.xml b/parts/res/drawable/ic_custom_seekbar_plus.xml
new file mode 100644
index 0000000..f3ebef7
--- /dev/null
+++ b/parts/res/drawable/ic_custom_seekbar_plus.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/parts/res/drawable/ic_custom_seekbar_reset.xml b/parts/res/drawable/ic_custom_seekbar_reset.xml
new file mode 100644
index 0000000..f280f36
--- /dev/null
+++ b/parts/res/drawable/ic_custom_seekbar_reset.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/parts/res/drawable/ic_dc_tile.xml b/parts/res/drawable/ic_dc_tile.xml
new file mode 100644
index 0000000..be6f6d3
--- /dev/null
+++ b/parts/res/drawable/ic_dc_tile.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/parts/res/drawable/ic_dcdimming.xml b/parts/res/drawable/ic_dcdimming.xml
new file mode 100644
index 0000000..d772b86
--- /dev/null
+++ b/parts/res/drawable/ic_dcdimming.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/parts/res/drawable/ic_hbm_tile.xml b/parts/res/drawable/ic_hbm_tile.xml
new file mode 100644
index 0000000..4eef425
--- /dev/null
+++ b/parts/res/drawable/ic_hbm_tile.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/parts/res/drawable/ic_info_outline.xml b/parts/res/drawable/ic_info_outline.xml
new file mode 100644
index 0000000..4aeef10
--- /dev/null
+++ b/parts/res/drawable/ic_info_outline.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/parts/res/layout/preference_custom_seekbar.xml b/parts/res/layout/preference_custom_seekbar.xml
new file mode 100644
index 0000000..ebd4684
--- /dev/null
+++ b/parts/res/layout/preference_custom_seekbar.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/values/attrs.xml b/parts/res/values/attrs.xml
new file mode 100644
index 0000000..443f580
--- /dev/null
+++ b/parts/res/values/attrs.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/values/custom_seekbar_strings.xml b/parts/res/values/custom_seekbar_strings.xml
new file mode 100644
index 0000000..d442353
--- /dev/null
+++ b/parts/res/values/custom_seekbar_strings.xml
@@ -0,0 +1,7 @@
+
+
+ Value: %s
+ by default
+ Default value: %s\nLong tap to set
+ Default value is set
+
diff --git a/parts/res/values/strings.xml b/parts/res/values/strings.xml
index 1387a8c..8783e4c 100644
--- a/parts/res/values/strings.xml
+++ b/parts/res/values/strings.xml
@@ -47,5 +47,23 @@
Clear speaker
Play a 30-second audio to clear the speaker
Run this feature once or twice if you find that your speaker is lightly blocked by dust. Set media volume to maximum.\n\nIf the speaker is blocked heavily, run this feature 2-5 times while shaking your device with the speaker facing downwards.
+
+
+ High brightness mode
+ Settings for HBM
+ HBM
+ Enable peak luminance
+ Automatic HBM
+ Enable peak luminance based on sunlight
+
+ threshold (lux)
+
+ disable delay (seconds)
+ Long time usage of High brightness mode may damage your display
+
+
+ DC Dimming
+ Reduces eye strain in low light conditions
+ DC Dimming is currently not supported by the kernel
diff --git a/parts/res/xml/dcdimming_settings.xml b/parts/res/xml/dcdimming_settings.xml
new file mode 100644
index 0000000..8ca09c3
--- /dev/null
+++ b/parts/res/xml/dcdimming_settings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/parts/res/xml/hbm_settings.xml b/parts/res/xml/hbm_settings.xml
new file mode 100644
index 0000000..a513f82
--- /dev/null
+++ b/parts/res/xml/hbm_settings.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/src/org/lineageos/settings/BootCompletedReceiver.java b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
index 4617b7d..0a27487 100644
--- a/parts/src/org/lineageos/settings/BootCompletedReceiver.java
+++ b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
@@ -21,21 +21,33 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+import android.content.SharedPreferences;
+import android.os.SystemProperties;
+import androidx.preference.PreferenceManager;
import org.lineageos.settings.doze.DozeUtils;
import org.lineageos.settings.thermal.ThermalUtils;
import org.lineageos.settings.refreshrate.RefreshUtils;
+import org.lineageos.settings.utils.FileUtils;
public class BootCompletedReceiver extends BroadcastReceiver {
private static final boolean DEBUG = false;
private static final String TAG = "XiaomiParts";
+ private static final String DC_DIMMING_ENABLE_KEY = "dc_dimming_enable";
+ private static final String DC_DIMMING_NODE = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure";
@Override
public void onReceive(final Context context, Intent intent) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
if (DEBUG)
Log.d(TAG, "Received boot completed intent");
DozeUtils.onBootCompleted(context);
ThermalUtils.startService(context);
- RefreshUtils.startService(context);
+ RefreshUtils.startService(context);
+
+ // DC Dimming
+ FileUtils.enableService(context);
+ boolean dcDimmingEnabled = sharedPrefs.getBoolean(DC_DIMMING_ENABLE_KEY, false);
+ FileUtils.writeLine(DC_DIMMING_NODE, dcDimmingEnabled ? "1" : "0");
}
}
diff --git a/parts/src/org/lineageos/settings/display/DcDimmingSettingsActivity.java b/parts/src/org/lineageos/settings/display/DcDimmingSettingsActivity.java
new file mode 100644
index 0000000..2b621e2
--- /dev/null
+++ b/parts/src/org/lineageos/settings/display/DcDimmingSettingsActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015-2016 The CyanogenMod Project
+ * 2017,2021-2022 The LineageOS 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 org.lineageos.settings.display;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class DcDimmingSettingsActivity extends CollapsingToolbarBaseActivity {
+
+ private static final String TAG_DCDIMMING = "dcdimming";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame,
+ new DcDimmingSettingsFragment(), TAG_DCDIMMING).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/display/DcDimmingSettingsFragment.java b/parts/src/org/lineageos/settings/display/DcDimmingSettingsFragment.java
new file mode 100644
index 0000000..8b6f83f
--- /dev/null
+++ b/parts/src/org/lineageos/settings/display/DcDimmingSettingsFragment.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.settings.display;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+
+import androidx.preference.SwitchPreference;
+import android.provider.Settings;
+
+import org.lineageos.settings.R;
+import org.lineageos.settings.utils.FileUtils;
+
+import java.io.File;
+
+public class DcDimmingSettingsFragment extends PreferenceFragment implements
+ OnPreferenceChangeListener {
+
+ private SwitchPreference mDcDimmingPreference;
+ private static final String DC_DIMMING_ENABLE_KEY = "dc_dimming_enable";
+ private static final String DC_DIMMING_NODE = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure";
+ private static final String HBM = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm";
+ private static final String HBM_KEY = "hbm";
+
+ private File hbmFile;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.dcdimming_settings, rootKey);
+ mDcDimmingPreference = findPreference(DC_DIMMING_ENABLE_KEY);
+ if (FileUtils.fileExists(DC_DIMMING_NODE)) {
+ mDcDimmingPreference.setEnabled(true);
+ mDcDimmingPreference.setOnPreferenceChangeListener(this);
+ } else {
+ mDcDimmingPreference.setSummary(R.string.dc_dimming_enable_summary_not_supported);
+ mDcDimmingPreference.setEnabled(false);
+ }
+ hbmFile = new File(HBM);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (DC_DIMMING_ENABLE_KEY.equals(preference.getKey())) {
+ boolean enabled = (boolean) newValue;
+ FileUtils.writeLine(DC_DIMMING_NODE, enabled ? "1" : "0");
+ if (enabled) {
+ disableHBM();
+ }
+ updateHBMPreference(!enabled);
+ }
+ return true;
+ }
+
+ private void disableHBM() {
+ // Disable HBM mode
+ FileUtils.writeLine(HBM, "0");
+ // Make HBM mode path read-only
+ hbmFile.setReadOnly();
+ // Update HBM mode UI tile
+ updateHBMUI(false);
+ }
+
+ private void updateHBMUI(boolean enabled) {
+ Intent intent = new Intent("org.lineageos.settings.hbm.UPDATE_TILE");
+ intent.putExtra("enabled", enabled);
+ getActivity().sendBroadcast(intent);
+ // Update HBM preference UI
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPrefs.edit().putBoolean(HBM_KEY, enabled).apply();
+ updateHBMPreference(enabled);
+ }
+
+ private void updateHBMPreference(boolean enabled) {
+ // Update HBM preference UI
+ if (mDcDimmingPreference != null) {
+ mDcDimmingPreference.setChecked(enabled);
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/display/DcDimmingTileService.java b/parts/src/org/lineageos/settings/display/DcDimmingTileService.java
new file mode 100644
index 0000000..d6a9350
--- /dev/null
+++ b/parts/src/org/lineageos/settings/display/DcDimmingTileService.java
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2018 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+package org.lineageos.settings.display;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import androidx.preference.PreferenceManager;
+import android.provider.Settings;
+
+import org.lineageos.settings.utils.FileUtils;
+
+import java.io.File;
+
+public class DcDimmingTileService extends TileService {
+
+ public static final String DC_DIMMING_ENABLE_KEY = "dc_dimming_enable";
+ private static final String DC_DIMMING_NODE = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure";
+ private static final String HBM = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm";
+ private static final String HBM_KEY = "hbm";
+
+ private File hbmFile;
+
+ private BroadcastReceiver screenStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ Editor editor = sharedPrefs.edit();
+ editor.putBoolean(DC_DIMMING_ENABLE_KEY, false);
+ editor.apply();
+ updateUI(false);
+ disableHBM();
+ }
+ }
+ };
+
+ private void updateUI(boolean enabled) {
+ final Tile tile = getQsTile();
+ tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ tile.updateTile();
+ }
+
+ private void disableHBM() {
+ // Disable HBM mode
+ FileUtils.writeLine(HBM, "0");
+ // Make HBM mode path read-only
+ hbmFile.setReadOnly();
+ // Update HBM mode UI tile
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ Editor editor = sharedPrefs.edit();
+ editor.putBoolean(HBM_KEY, false);
+ editor.apply();
+ updateHBMUI(false);
+ }
+
+ private void updateHBMUI(boolean enabled) {
+ Intent intent = new Intent("org.lineageos.settings.hbm.UPDATE_TILE");
+ intent.putExtra("enabled", enabled);
+ sendBroadcast(intent);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(screenStateReceiver, filter);
+ hbmFile = new File(HBM);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(screenStateReceiver);
+ }
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ updateUI(sharedPrefs.getBoolean(DC_DIMMING_ENABLE_KEY, false));
+ }
+
+ @Override
+ public void onStopListening() {
+ super.onStopListening();
+ }
+
+ @Override
+ public void onClick() {
+ super.onClick();
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ final boolean enabled = !(sharedPrefs.getBoolean(DC_DIMMING_ENABLE_KEY, false));
+ FileUtils.writeLine(DC_DIMMING_NODE, enabled ? "1" : "0");
+ if (enabled) {
+ disableHBM();
+ } else {
+ hbmFile.setWritable(true);
+ }
+ sharedPrefs.edit().putBoolean(DC_DIMMING_ENABLE_KEY, enabled).apply();
+ updateUI(enabled);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/AutoHBMService.java b/parts/src/org/lineageos/settings/hbm/AutoHBMService.java
new file mode 100644
index 0000000..20712c7
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/AutoHBMService.java
@@ -0,0 +1,155 @@
+package org.lineageos.settings.hbm;
+
+import android.app.KeyguardManager;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.IBinder;
+import android.os.PowerManager;
+import androidx.preference.PreferenceManager;
+import android.provider.Settings;
+
+import org.lineageos.settings.utils.FileUtils;
+import org.lineageos.settings.display.*;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class AutoHBMService extends Service {
+ private static final String HBM = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled";
+ private static final String BACKLIGHT = "/sys/class/backlight/panel0-backlight/brightness";
+
+ private static boolean mAutoHBMActive = false;
+ private ExecutorService mExecutorService;
+
+ private SensorManager mSensorManager;
+ Sensor mLightSensor;
+
+ private SharedPreferences mSharedPrefs;
+ private boolean dcDimmingEnabled;
+
+ public void activateLightSensorRead() {
+ submit(() -> {
+ mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ mSensorManager.registerListener(mSensorEventListener, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ });
+ }
+
+ public void deactivateLightSensorRead() {
+ submit(() -> {
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mAutoHBMActive = false;
+ enableHBM(false);
+ });
+ }
+
+ private void enableHBM(boolean enable) {
+ if (enable) {
+ FileUtils.writeLine(HBM, "1");
+ FileUtils.writeLine(BACKLIGHT, "2047");
+ Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 255);
+ } else {
+ FileUtils.writeLine(HBM, "0");
+ }
+ }
+
+ private boolean isCurrentlyEnabled() {
+ return FileUtils.getFileValueAsBoolean(HBM, false);
+ }
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float lux = event.values[0];
+ KeyguardManager km =
+ (KeyguardManager) getSystemService(getApplicationContext().KEYGUARD_SERVICE);
+ boolean keyguardShowing = km.inKeyguardRestrictedInputMode();
+ float luxThreshold = Float.parseFloat(mSharedPrefs.getString(HBMFragment.KEY_AUTO_HBM_THRESHOLD, "20000"));
+ long timeToDisableHBM = Long.parseLong(mSharedPrefs.getString(HBMFragment.KEY_HBM_DISABLE_TIME, "1"));
+
+ if (lux > luxThreshold) {
+ if ((!mAutoHBMActive || !isCurrentlyEnabled()) && !keyguardShowing && !dcDimmingEnabled) {
+ mAutoHBMActive = true;
+ enableHBM(true);
+ }
+ }
+ if (lux < luxThreshold) {
+ if (mAutoHBMActive) {
+ mExecutorService.submit(() -> {
+ try {
+ Thread.sleep(timeToDisableHBM * 1000);
+ } catch (InterruptedException e) {
+ }
+ if (lux < luxThreshold) {
+ mAutoHBMActive = false;
+ enableHBM(false);
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // do nothing
+ }
+ };
+
+ private BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ activateLightSensorRead();
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ deactivateLightSensorRead();
+ }
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ mExecutorService = Executors.newSingleThreadExecutor();
+ IntentFilter screenStateFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+ screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(mScreenStateReceiver, screenStateFilter);
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ if (pm.isInteractive()) {
+ activateLightSensorRead();
+ }
+ }
+
+ private Future < ? > submit(Runnable runnable) {
+ return mExecutorService.submit(runnable);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mScreenStateReceiver);
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ if (pm.isInteractive()) {
+ deactivateLightSensorRead();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/AutoHBMThresholdPreference.java b/parts/src/org/lineageos/settings/hbm/AutoHBMThresholdPreference.java
new file mode 100644
index 0000000..fa41bd7
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/AutoHBMThresholdPreference.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2016 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+package org.lineageos.settings.hbm;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceViewHolder;
+
+public class AutoHBMThresholdPreference extends CustomSeekBarPreference {
+
+ private static int mMinVal = 0;
+ private static int mMaxVal = 60000;
+ private static int mDefVal = 20000;
+
+ public AutoHBMThresholdPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mInterval = 1000;
+ mShowSign = false;
+ mUnits = "";
+ mContinuousUpdates = false;
+ mMinValue = mMinVal;
+ mMaxValue = mMaxVal;
+ mDefaultValueExists = true;
+ mDefaultValue = mDefVal;
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ mValue = Integer.parseInt(sharedPrefs.getString(HBMFragment.KEY_AUTO_HBM_THRESHOLD, "20000"));
+
+ setPersistent(false);
+ }
+
+ @Override
+ protected void changeValue(int newValue) {
+ SharedPreferences.Editor prefChange = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
+ prefChange.putString(HBMFragment.KEY_AUTO_HBM_THRESHOLD, String.valueOf(newValue)).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/AutoHBMTimePreference.java b/parts/src/org/lineageos/settings/hbm/AutoHBMTimePreference.java
new file mode 100644
index 0000000..63b1615
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/AutoHBMTimePreference.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The OmniROM Project
+ 2023 The Evolution X Project
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+package org.lineageos.settings.hbm;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceViewHolder;
+
+public class AutoHBMTimePreference extends CustomSeekBarPreference {
+
+ private static int mMinVal = 1;
+ private static int mMaxVal = 10;
+ private static int mDefVal = 1;
+
+ public AutoHBMTimePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mInterval = 1;
+ mShowSign = false;
+ mUnits = "";
+ mContinuousUpdates = false;
+ mMinValue = mMinVal;
+ mMaxValue = mMaxVal;
+ mDefaultValueExists = true;
+ mDefaultValue = mDefVal;
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ mValue = Integer.parseInt(sharedPrefs.getString(HBMFragment.KEY_HBM_DISABLE_TIME, "1"));
+
+ setPersistent(false);
+ }
+
+ @Override
+ protected void changeValue(int newValue) {
+ SharedPreferences.Editor prefChange = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
+ prefChange.putString(HBMFragment.KEY_HBM_DISABLE_TIME, String.valueOf(newValue)).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/CustomSeekBarPreference.java b/parts/src/org/lineageos/settings/hbm/CustomSeekBarPreference.java
new file mode 100644
index 0000000..50324e1
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/CustomSeekBarPreference.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2016-2021 crDroid Android 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 org.lineageos.settings.hbm;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.*;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.lineageos.settings.R;
+
+public class CustomSeekBarPreference extends Preference implements SeekBar.OnSeekBarChangeListener {
+ protected final String TAG = getClass().getName();
+ private static final String SETTINGS_NS = "http://schemas.android.com/apk/res/com.android.settings";
+ protected static final String ANDROIDNS = "http://schemas.android.com/apk/res/android";
+
+ protected int mInterval = 1;
+ protected boolean mShowSign = false;
+ protected String mUnits = "";
+ protected boolean mContinuousUpdates = false;
+
+ protected int mMinValue = 0;
+ protected int mMaxValue = 100;
+ protected boolean mDefaultValueExists = false;
+ protected int mDefaultValue;
+ protected boolean mDefaultValueTextExists = false;
+ protected String mDefaultValueText;
+
+ protected int mValue;
+
+ protected TextView mValueTextView;
+ protected ImageView mResetImageView;
+ protected ImageView mMinusImageView;
+ protected ImageView mPlusImageView;
+ protected SeekBar mSeekBar;
+
+ protected boolean mTrackingTouch = false;
+ protected int mTrackingValue;
+
+ public CustomSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomSeekBarPreference);
+ try {
+ mShowSign = a.getBoolean(R.styleable.CustomSeekBarPreference_showSign, mShowSign);
+ String units = a.getString(R.styleable.CustomSeekBarPreference_units);
+ if (units != null)
+ mUnits = " " + units;
+ mContinuousUpdates = a.getBoolean(R.styleable.CustomSeekBarPreference_continuousUpdates, mContinuousUpdates);
+ String defaultValueText = a.getString(R.styleable.CustomSeekBarPreference_defaultValueText);
+ mDefaultValueTextExists = defaultValueText != null && !defaultValueText.isEmpty();
+ if (mDefaultValueTextExists) {
+ mDefaultValueText = defaultValueText;
+ }
+ } finally {
+ a.recycle();
+ }
+
+ try {
+ String newInterval = attrs.getAttributeValue(SETTINGS_NS, "interval");
+ if (newInterval != null)
+ mInterval = Integer.parseInt(newInterval);
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid interval value", e);
+ }
+ mMinValue = attrs.getAttributeIntValue(SETTINGS_NS, "min", mMinValue);
+ mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", mMaxValue);
+ if (mMaxValue < mMinValue)
+ mMaxValue = mMinValue;
+ String defaultValue = attrs.getAttributeValue(ANDROIDNS, "defaultValue");
+ mDefaultValueExists = defaultValue != null && !defaultValue.isEmpty();
+ if (mDefaultValueExists) {
+ mDefaultValue = getLimitedValue(Integer.parseInt(defaultValue));
+ mValue = mDefaultValue;
+ } else {
+ mValue = mMinValue;
+ }
+
+ mSeekBar = new SeekBar(context, attrs);
+ setLayoutResource(R.layout.preference_custom_seekbar);
+ }
+
+ public CustomSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public CustomSeekBarPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context,
+ androidx.preference.R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
+ }
+
+ public CustomSeekBarPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void onDependencyChanged(Preference dependency, boolean disableDependent) {
+ super.onDependencyChanged(dependency, disableDependent);
+ this.setShouldDisableView(true);
+ if (mSeekBar != null)
+ mSeekBar.setEnabled(!disableDependent);
+ if (mResetImageView != null)
+ mResetImageView.setEnabled(!disableDependent);
+ if (mPlusImageView != null)
+ mPlusImageView.setEnabled(!disableDependent);
+ if (mMinusImageView != null)
+ mMinusImageView.setEnabled(!disableDependent);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ try
+ {
+ // move our seekbar to the new view we've been given
+ ViewParent oldContainer = mSeekBar.getParent();
+ ViewGroup newContainer = (ViewGroup) holder.findViewById(R.id.seekbar);
+ if (oldContainer != newContainer) {
+ // remove the seekbar from the old view
+ if (oldContainer != null) {
+ ((ViewGroup) oldContainer).removeView(mSeekBar);
+ }
+ // remove the existing seekbar (there may not be one) and add ours
+ newContainer.removeAllViews();
+ newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Error binding view: " + ex.toString());
+ }
+
+ mSeekBar.setMax(getSeekValue(mMaxValue));
+ mSeekBar.setProgress(getSeekValue(mValue));
+ mSeekBar.setEnabled(isEnabled());
+
+ mValueTextView = (TextView) holder.findViewById(R.id.value);
+ mResetImageView = (ImageView) holder.findViewById(R.id.reset);
+ mMinusImageView = (ImageView) holder.findViewById(R.id.minus);
+ mPlusImageView = (ImageView) holder.findViewById(R.id.plus);
+
+ updateValueViews();
+
+ mSeekBar.setOnSeekBarChangeListener(this);
+ mResetImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Toast.makeText(getContext(), getContext().getString(R.string.custom_seekbar_default_value_to_set, getTextValue(mDefaultValue)),
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ mResetImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mDefaultValue, true);
+ return true;
+ }
+ });
+ mMinusImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ setValue(mValue - mInterval, true);
+ }
+ });
+ mMinusImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue < mValue * 2 ? Math.floorDiv(mMaxValue + mMinValue, 2) : mMinValue, true);
+ return true;
+ }
+ });
+ mPlusImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ setValue(mValue + mInterval, true);
+ }
+ });
+ mPlusImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue > mValue * 2 ? -1 * Math.floorDiv(-1 * (mMaxValue + mMinValue), 2) : mMaxValue, true);
+ return true;
+ }
+ });
+ }
+
+ protected int getLimitedValue(int v) {
+ return v < mMinValue ? mMinValue : (v > mMaxValue ? mMaxValue : v);
+ }
+
+ protected int getSeekValue(int v) {
+ return 0 - Math.floorDiv(mMinValue - v, mInterval);
+ }
+
+ protected String getTextValue(int v) {
+ if (mDefaultValueTextExists && mDefaultValueExists && v == mDefaultValue) {
+ return mDefaultValueText;
+ }
+ return (mShowSign && v > 0 ? "+" : "") + String.valueOf(v) + mUnits;
+ }
+
+ protected void updateValueViews() {
+ if (mValueTextView != null) {
+ if (!mTrackingTouch || mContinuousUpdates) {
+ if (mDefaultValueTextExists && mDefaultValueExists && mValue == mDefaultValue) {
+ mValueTextView.setText(mDefaultValueText + " (" +
+ getContext().getString(R.string.custom_seekbar_default_value) + ")");
+ } else {
+ mValueTextView.setText(getContext().getString(R.string.custom_seekbar_value, getTextValue(mValue)) +
+ (mDefaultValueExists && mValue == mDefaultValue ? " (" +
+ getContext().getString(R.string.custom_seekbar_default_value) + ")" : ""));
+ }
+ } else {
+ if (mDefaultValueTextExists && mDefaultValueExists && mTrackingValue == mDefaultValue) {
+ mValueTextView.setText("[" + mDefaultValueText + "]");
+ } else {
+ mValueTextView.setText(getContext().getString(R.string.custom_seekbar_value, "[" + getTextValue(mTrackingValue) + "]"));
+ }
+ }
+ }
+ if (mResetImageView != null) {
+ if (!mDefaultValueExists || mValue == mDefaultValue || mTrackingTouch)
+ mResetImageView.setVisibility(View.INVISIBLE);
+ else
+ mResetImageView.setVisibility(View.VISIBLE);
+ }
+ if (mMinusImageView != null) {
+ if (mValue == mMinValue || mTrackingTouch) {
+ mMinusImageView.setClickable(false);
+ mMinusImageView.setColorFilter(getContext().getColor(R.color.disabled_text_color),
+ PorterDuff.Mode.MULTIPLY);
+ } else {
+ mMinusImageView.setClickable(true);
+ mMinusImageView.clearColorFilter();
+ }
+ }
+ if (mPlusImageView != null) {
+ if (mValue == mMaxValue || mTrackingTouch) {
+ mPlusImageView.setClickable(false);
+ mPlusImageView.setColorFilter(getContext().getColor(R.color.disabled_text_color), PorterDuff.Mode.MULTIPLY);
+ } else {
+ mPlusImageView.setClickable(true);
+ mPlusImageView.clearColorFilter();
+ }
+ }
+ }
+
+ protected void changeValue(int newValue) {
+ // for subclasses
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ int newValue = getLimitedValue(mMinValue + (progress * mInterval));
+ if (mTrackingTouch && !mContinuousUpdates) {
+ mTrackingValue = newValue;
+ updateValueViews();
+ } else if (mValue != newValue) {
+ // change rejected, revert to the previous value
+ if (!callChangeListener(newValue)) {
+ mSeekBar.setProgress(getSeekValue(mValue));
+ return;
+ }
+ // change accepted, store it
+ changeValue(newValue);
+ persistInt(newValue);
+
+ mValue = newValue;
+ updateValueViews();
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mTrackingValue = mValue;
+ mTrackingTouch = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mTrackingTouch = false;
+ if (!mContinuousUpdates)
+ onProgressChanged(mSeekBar, getSeekValue(mTrackingValue), false);
+ notifyChanged();
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ if (restoreValue)
+ mValue = getPersistedInt(mValue);
+ }
+
+ @Override
+ public void setDefaultValue(Object defaultValue) {
+ if (defaultValue instanceof Integer)
+ setDefaultValue((Integer) defaultValue, mSeekBar != null);
+ else
+ setDefaultValue(defaultValue == null ? (String) null : defaultValue.toString(), mSeekBar != null);
+ }
+
+ public void setDefaultValue(int newValue, boolean update) {
+ newValue = getLimitedValue(newValue);
+ if (!mDefaultValueExists || mDefaultValue != newValue) {
+ mDefaultValueExists = true;
+ mDefaultValue = newValue;
+ if (update)
+ updateValueViews();
+ }
+ }
+
+ public void setDefaultValue(String newValue, boolean update) {
+ if (mDefaultValueExists && (newValue == null || newValue.isEmpty())) {
+ mDefaultValueExists = false;
+ if (update)
+ updateValueViews();
+ } else if (newValue != null && !newValue.isEmpty()) {
+ setDefaultValue(Integer.parseInt(newValue), update);
+ }
+ }
+
+ public void setValue(int newValue) {
+ mValue = getLimitedValue(newValue);
+ if (mSeekBar != null) mSeekBar.setProgress(getSeekValue(mValue));
+ }
+
+ public void setValue(int newValue, boolean update) {
+ newValue = getLimitedValue(newValue);
+ if (mValue != newValue) {
+ if (update)
+ mSeekBar.setProgress(getSeekValue(newValue));
+ else
+ mValue = newValue;
+ }
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public void refresh(int newValue) {
+ // this will ...
+ setValue(newValue, mSeekBar != null);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/HBMActivity.java b/parts/src/org/lineageos/settings/hbm/HBMActivity.java
new file mode 100644
index 0000000..67ce514
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/HBMActivity.java
@@ -0,0 +1,45 @@
+/*
+* Copyright (C) 2017 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+package org.lineageos.settings.hbm;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceManager;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class HBMActivity extends CollapsingToolbarBaseActivity {
+
+ private HBMFragment mHBMFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Fragment fragment = getFragmentManager().findFragmentById(com.android.settingslib.collapsingtoolbar.R.id.content_frame);
+ if (fragment == null) {
+ mHBMFragment = new HBMFragment();
+ getFragmentManager().beginTransaction()
+ .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame, mHBMFragment)
+ .commit();
+ } else {
+ mHBMFragment = (HBMFragment) fragment;
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/HBMFragment.java b/parts/src/org/lineageos/settings/hbm/HBMFragment.java
new file mode 100644
index 0000000..b80491d
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/HBMFragment.java
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2016 The OmniROM Project
+* Copyright (C) 2018-2021 crDroid Android Project
+* Copyright (C) 2019-2022 Evolution X Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+package org.lineageos.settings.hbm;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceManager;
+import androidx.preference.TwoStatePreference;
+
+import org.lineageos.settings.utils.FileUtils;
+import org.lineageos.settings.R;
+
+public class HBMFragment extends PreferenceFragment
+ implements Preference.OnPreferenceChangeListener {
+ private static final String TAG = HBMFragment.class.getSimpleName();
+
+ public static final String KEY_HBM_SWITCH = "hbm";
+ public static final String KEY_AUTO_HBM_SWITCH = "auto_hbm";
+ public static final String KEY_AUTO_HBM_THRESHOLD = "auto_hbm_threshold";
+ public static final String KEY_HBM_DISABLE_TIME = "hbm_disable_time";
+
+ private static TwoStatePreference mHBMModeSwitch;
+ private static TwoStatePreference mAutoHBMSwitch;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getContext());
+ addPreferencesFromResource(R.xml.hbm_settings);
+
+ // HBM
+ mHBMModeSwitch = (TwoStatePreference) findPreference(KEY_HBM_SWITCH);
+ mHBMModeSwitch.setOnPreferenceChangeListener(new HBMModeSwitch(getContext()));
+
+ // AutoHBM
+ mAutoHBMSwitch = (TwoStatePreference) findPreference(KEY_AUTO_HBM_SWITCH);
+ mAutoHBMSwitch.setOnPreferenceChangeListener(this);
+ mAutoHBMSwitch.setChecked(PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean(HBMFragment.KEY_AUTO_HBM_SWITCH, false));
+ }
+
+ public static boolean isAUTOHBMEnabled(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(HBMFragment.KEY_AUTO_HBM_SWITCH, false);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mAutoHBMSwitch) {
+ Boolean enabled = (Boolean) newValue;
+ SharedPreferences.Editor prefChange = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
+ prefChange.putBoolean(KEY_AUTO_HBM_SWITCH, enabled).commit();
+ FileUtils.enableService(getContext());
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/HBMModeSwitch.java b/parts/src/org/lineageos/settings/hbm/HBMModeSwitch.java
new file mode 100644
index 0000000..cd7e8ca
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/HBMModeSwitch.java
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2016 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+package org.lineageos.settings.hbm;
+
+import android.provider.Settings;
+import android.content.Context;
+import android.content.SharedPreferences;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceManager;
+
+import org.lineageos.settings.utils.FileUtils;
+import org.lineageos.settings.display.*;
+
+public class HBMModeSwitch implements OnPreferenceChangeListener {
+ private static final String HBM = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled";
+ private static final String BACKLIGHT = "/sys/class/backlight/panel0-backlight/brightness";
+ private Context mContext;
+
+ public HBMModeSwitch(Context context) {
+ mContext = context;
+ }
+
+ public static String getHBM() {
+ if (FileUtils.isFileWritable(HBM)) {
+ return HBM;
+ }
+ return null;
+ }
+
+ public static String getBACKLIGHT() {
+ if (FileUtils.isFileWritable(BACKLIGHT)) {
+ return BACKLIGHT;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Boolean enabled = (Boolean) newValue;
+ boolean dcDimmingEnabled = PreferenceManager.getDefaultSharedPreferences(mContext).getBoolean(DcDimmingTileService.DC_DIMMING_ENABLE_KEY, false);
+ if (dcDimmingEnabled) {
+ return false;
+ }
+ FileUtils.writeLine(getHBM(), enabled ? "1" : "0");
+ if (enabled) {
+ FileUtils.writeLine(getBACKLIGHT(), "2047");
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 255);
+ }
+ return true;
+ }
+}
diff --git a/parts/src/org/lineageos/settings/hbm/HBMModeTileService.java b/parts/src/org/lineageos/settings/hbm/HBMModeTileService.java
new file mode 100644
index 0000000..dfdef17
--- /dev/null
+++ b/parts/src/org/lineageos/settings/hbm/HBMModeTileService.java
@@ -0,0 +1,100 @@
+/*
+* Copyright (C) 2018 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*/
+
+package org.lineageos.settings.hbm;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.BroadcastReceiver;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import androidx.preference.PreferenceManager;
+import android.provider.Settings;
+
+import org.lineageos.settings.utils.FileUtils;
+import org.lineageos.settings.display.*;
+
+public class HBMModeTileService extends TileService {
+
+ private static final String HBM = "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled";
+ private static final String HBM_KEY = "hbm";
+ private static final String BACKLIGHT = "/sys/class/backlight/panel0-backlight/brightness";
+
+ private BroadcastReceiver screenStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ sharedPrefs.edit().putBoolean(HBM_KEY, false).commit();
+ updateUI(false);
+ }
+ }
+ };
+
+ private void updateUI(boolean enabled) {
+ final Tile tile = getQsTile();
+ tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ tile.updateTile();
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(screenStateReceiver, filter);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(screenStateReceiver);
+ }
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ updateUI(sharedPrefs.getBoolean(HBM_KEY, false));
+ }
+
+ @Override
+ public void onStopListening() {
+ super.onStopListening();
+ }
+ @Override
+ public void onClick() {
+ super.onClick();
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ final boolean dcDimmingEnabled = sharedPrefs.getBoolean(DcDimmingTileService.DC_DIMMING_ENABLE_KEY, false);
+ if (dcDimmingEnabled) {
+ return;
+ }
+ final boolean enabled = !(sharedPrefs.getBoolean(HBM_KEY, false));
+ FileUtils.writeLine(HBM, enabled ? "1" : "0");
+ if (enabled) {
+ FileUtils.writeLine(BACKLIGHT, "2047");
+ Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 255);
+ }
+ sharedPrefs.edit().putBoolean(HBM_KEY, enabled).commit();
+ updateUI(enabled);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/utils/FileUtils.java b/parts/src/org/lineageos/settings/utils/FileUtils.java
index 00028ff..26339c2 100644
--- a/parts/src/org/lineageos/settings/utils/FileUtils.java
+++ b/parts/src/org/lineageos/settings/utils/FileUtils.java
@@ -18,6 +18,10 @@ package org.lineageos.settings.utils;
import android.util.Log;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -26,6 +30,9 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import org.lineageos.settings.hbm.HBMFragment;
+import org.lineageos.settings.hbm.AutoHBMService;
+
public final class FileUtils {
private static final String TAG = "FileUtils";
@@ -156,5 +163,38 @@ public final class FileUtils {
Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e);
}
return ok;
+ }
+
+ public static boolean getFileValueAsBoolean(String filename, boolean defValue) {
+ String fileValue = readOneLine(filename);
+ if(fileValue!=null){
+ return (fileValue.equals("0")?false:true);
+ }
+ return defValue;
+ }
+ public static String getFileValue(String filename, String defValue) {
+ String fileValue = readOneLine(filename);
+ if(fileValue!=null){
+ return fileValue;
+ }
+ return defValue;
+ }
+ private static boolean mServiceEnabled = false;
+ private static void startService(Context context) {
+ context.startServiceAsUser(new Intent(context, AutoHBMService.class),
+ UserHandle.CURRENT);
+ mServiceEnabled = true;
+ }
+ private static void stopService(Context context) {
+ mServiceEnabled = false;
+ context.stopServiceAsUser(new Intent(context, AutoHBMService.class),
+ UserHandle.CURRENT);
+ }
+ public static void enableService(Context context) {
+ if (HBMFragment.isAUTOHBMEnabled(context) && !mServiceEnabled) {
+ startService(context);
+ } else if (!HBMFragment.isAUTOHBMEnabled(context) && mServiceEnabled) {
+ stopService(context);
+ }
}
}
diff --git a/rootdir/etc/init.qcom.rc b/rootdir/etc/init.qcom.rc
index a5242f0..1110caa 100644
--- a/rootdir/etc/init.qcom.rc
+++ b/rootdir/etc/init.qcom.rc
@@ -220,6 +220,13 @@ on post-fs-data
mkdir /data/vendor/vpp 0770 media media
setprop vold.post_fs_data_done 1
+
+ # Allow system to modify exposure dim layer state
+ chown system system /sys/devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure
+ chmod 0660 /sys/devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure
+ # Allow system to modify hbm state
+ chown system system /sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled
+ chmod 0660 /sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled
on property:sys.boot_completed=1
# Enable UFS clock scaling back
diff --git a/sepolicy/vendor/genfs_contexts b/sepolicy/vendor/genfs_contexts
index 4e1f399..b75510e 100644
--- a/sepolicy/vendor/genfs_contexts
+++ b/sepolicy/vendor/genfs_contexts
@@ -1,6 +1,12 @@
# Display
genfscon sysfs /devices/platform/soc/soc:qcom,dsi-display-primary u:object_r:vendor_sysfs_graphics:s0
+# Display dim layer Exposure
+genfscon sysfs /devices/platform/soc/soc:qcom,dsi-display-primary/dimlayer_exposure u:object_r:vendor_sysfs_graphics:s0
+
+# Display HBM
+genfscon sysfs /sys/devices/platform/soc/soc:qcom,dsi-display-primary/hbm_enabled u:object_r:vendor_sysfs_graphics:s0
+
# Fingerprint
genfscon sysfs /devices/platform/soc/soc:fingerprint_fpc u:object_r:vendor_sysfs_fingerprint:s0
diff --git a/sepolicy/vendor/system_app.te b/sepolicy/vendor/system_app.te
index 667cda8..02c39a7 100644
--- a/sepolicy/vendor/system_app.te
+++ b/sepolicy/vendor/system_app.te
@@ -1,2 +1,6 @@
allow system_app sysfs_thermal:file write;
allow system_app sysfs_thermal:file rw_file_perms;
+
+# DC Dimming and HBM
+allow system_app vendor_sysfs_graphics:dir search;
+allow system_app vendor_sysfs_graphics:file { getattr open write };
diff --git a/sepolicy/vendor/vendor_hal_perf_default.te b/sepolicy/vendor/vendor_hal_perf_default.te
new file mode 100644
index 0000000..0711d9a
--- /dev/null
+++ b/sepolicy/vendor/vendor_hal_perf_default.te
@@ -0,0 +1,2 @@
+allow vendor_hal_perf_default hal_graphics_composer_default:dir search;
+allow vendor_hal_perf_default hal_graphics_composer_default:file { open read };