From e814155dd70c98d3e8a0e4805fe39a2a0e53869d Mon Sep 17 00:00:00 2001 From: Arian Date: Thu, 14 Mar 2024 00:14:05 +0530 Subject: [PATCH] sm8350-common: parts: Import from xiaomi_sdm845-common https://github.com/LineageOS/android_device_xiaomi_sdm845-common/tree/e261035d91b34cf0fdf0f012927e06a2bcdb03ed/parts Change-Id: I442f48780ca020f3e420ee4ccaf8ac07b05457cb Signed-off-by: Anush02198 --- common.mk | 4 + .../apps/Settings/res/values/config.xml | 3 + parts/Android.bp | 25 + parts/AndroidManifest.xml | 115 +++++ parts/proguard.flags | 3 + parts/res/drawable/ic_clear_speaker.xml | 13 + parts/res/drawable/ic_doze_aod.xml | 9 + .../res/drawable/ic_doze_brightness_auto.xml | 9 + .../res/drawable/ic_doze_brightness_high.xml | 9 + parts/res/drawable/ic_doze_brightness_low.xml | 9 + parts/res/drawable/ic_refresh_120.xml | 22 + parts/res/drawable/ic_refresh_60.xml | 17 + parts/res/drawable/ic_refresh_default.xml | 11 + parts/res/drawable/ic_scenes.xml | 9 + parts/res/drawable/ic_thermal_benchmark.xml | 19 + parts/res/drawable/ic_thermal_browser.xml | 11 + parts/res/drawable/ic_thermal_camera.xml | 19 + parts/res/drawable/ic_thermal_default.xml | 19 + parts/res/drawable/ic_thermal_dialer.xml | 18 + parts/res/drawable/ic_thermal_gaming.xml | 19 + parts/res/drawable/ic_thermal_streaming.xml | 19 + parts/res/layout/refresh_layout.xml | 18 + parts/res/layout/refresh_list_item.xml | 64 +++ parts/res/layout/thermal_layout.xml | 18 + parts/res/layout/thermal_list_item.xml | 64 +++ parts/res/raw/clear_speaker_sound.mp3 | Bin 0 -> 56654 bytes parts/res/values/arrays.xml | 28 ++ parts/res/values/strings.xml | 49 ++ parts/res/xml/clear_speaker_settings.xml | 17 + parts/res/xml/doze_settings.xml | 42 ++ .../settings/BootCompletedReceiver.java | 41 ++ .../lineageos/settings/doze/AodSensor.java | 80 ++++ .../lineageos/settings/doze/DozeService.java | 92 ++++ .../settings/doze/DozeSettingsActivity.java | 35 ++ .../settings/doze/DozeSettingsFragment.java | 156 +++++++ .../lineageos/settings/doze/DozeUtils.java | 155 +++++++ .../settings/refreshrate/RefreshActivity.java | 34 ++ .../settings/refreshrate/RefreshService.java | 102 +++++ .../refreshrate/RefreshSettingsFragment.java | 419 +++++++++++++++++ .../settings/refreshrate/RefreshUtils.java | 141 ++++++ .../speaker/ClearSpeakerActivity.java | 35 ++ .../speaker/ClearSpeakerFragment.java | 121 +++++ .../settings/thermal/ThermalActivity.java | 33 ++ .../settings/thermal/ThermalService.java | 96 ++++ .../thermal/ThermalSettingsFragment.java | 428 ++++++++++++++++++ .../settings/thermal/ThermalUtils.java | 165 +++++++ .../lineageos/settings/utils/FileUtils.java | 160 +++++++ 47 files changed, 2975 insertions(+) create mode 100644 parts/Android.bp create mode 100644 parts/AndroidManifest.xml create mode 100644 parts/proguard.flags create mode 100644 parts/res/drawable/ic_clear_speaker.xml create mode 100644 parts/res/drawable/ic_doze_aod.xml create mode 100644 parts/res/drawable/ic_doze_brightness_auto.xml create mode 100644 parts/res/drawable/ic_doze_brightness_high.xml create mode 100644 parts/res/drawable/ic_doze_brightness_low.xml create mode 100644 parts/res/drawable/ic_refresh_120.xml create mode 100644 parts/res/drawable/ic_refresh_60.xml create mode 100644 parts/res/drawable/ic_refresh_default.xml create mode 100644 parts/res/drawable/ic_scenes.xml create mode 100644 parts/res/drawable/ic_thermal_benchmark.xml create mode 100644 parts/res/drawable/ic_thermal_browser.xml create mode 100644 parts/res/drawable/ic_thermal_camera.xml create mode 100644 parts/res/drawable/ic_thermal_default.xml create mode 100644 parts/res/drawable/ic_thermal_dialer.xml create mode 100644 parts/res/drawable/ic_thermal_gaming.xml create mode 100644 parts/res/drawable/ic_thermal_streaming.xml create mode 100644 parts/res/layout/refresh_layout.xml create mode 100644 parts/res/layout/refresh_list_item.xml create mode 100644 parts/res/layout/thermal_layout.xml create mode 100644 parts/res/layout/thermal_list_item.xml create mode 100644 parts/res/raw/clear_speaker_sound.mp3 create mode 100644 parts/res/values/arrays.xml create mode 100644 parts/res/values/strings.xml create mode 100644 parts/res/xml/clear_speaker_settings.xml create mode 100644 parts/res/xml/doze_settings.xml create mode 100644 parts/src/org/lineageos/settings/BootCompletedReceiver.java create mode 100644 parts/src/org/lineageos/settings/doze/AodSensor.java create mode 100644 parts/src/org/lineageos/settings/doze/DozeService.java create mode 100644 parts/src/org/lineageos/settings/doze/DozeSettingsActivity.java create mode 100644 parts/src/org/lineageos/settings/doze/DozeSettingsFragment.java create mode 100644 parts/src/org/lineageos/settings/doze/DozeUtils.java create mode 100644 parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java create mode 100644 parts/src/org/lineageos/settings/refreshrate/RefreshService.java create mode 100644 parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java create mode 100644 parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java create mode 100644 parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java create mode 100644 parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java create mode 100644 parts/src/org/lineageos/settings/thermal/ThermalActivity.java create mode 100644 parts/src/org/lineageos/settings/thermal/ThermalService.java create mode 100644 parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java create mode 100644 parts/src/org/lineageos/settings/thermal/ThermalUtils.java create mode 100644 parts/src/org/lineageos/settings/utils/FileUtils.java diff --git a/common.mk b/common.mk index b245ce2..b25b517 100644 --- a/common.mk +++ b/common.mk @@ -128,6 +128,10 @@ PRODUCT_COPY_FILES += \ # DebugFS PRODUCT_SET_DEBUGFS_RESTRICTIONS := true +# Device-specific settings +PRODUCT_PACKAGES += \ + XiaomiParts + # Display PRODUCT_PACKAGES += \ android.hardware.graphics.mapper@3.0-impl-qti-display \ diff --git a/overlay/packages/apps/Settings/res/values/config.xml b/overlay/packages/apps/Settings/res/values/config.xml index 5ab8148..0d4e860 100644 --- a/overlay/packages/apps/Settings/res/values/config.xml +++ b/overlay/packages/apps/Settings/res/values/config.xml @@ -15,6 +15,9 @@ --> + + org.lineageos.settings/org.lineageos.settings.doze.DozeSettingsActivity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parts/proguard.flags b/parts/proguard.flags new file mode 100644 index 0000000..f6d7a5b --- /dev/null +++ b/parts/proguard.flags @@ -0,0 +1,3 @@ +-keep class org.lineageos.settings.doze.* { + *; +} diff --git a/parts/res/drawable/ic_clear_speaker.xml b/parts/res/drawable/ic_clear_speaker.xml new file mode 100644 index 0000000..4e2981d --- /dev/null +++ b/parts/res/drawable/ic_clear_speaker.xml @@ -0,0 +1,13 @@ + + + + diff --git a/parts/res/drawable/ic_doze_aod.xml b/parts/res/drawable/ic_doze_aod.xml new file mode 100644 index 0000000..a54ee17 --- /dev/null +++ b/parts/res/drawable/ic_doze_aod.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_doze_brightness_auto.xml b/parts/res/drawable/ic_doze_brightness_auto.xml new file mode 100644 index 0000000..81d74f5 --- /dev/null +++ b/parts/res/drawable/ic_doze_brightness_auto.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_doze_brightness_high.xml b/parts/res/drawable/ic_doze_brightness_high.xml new file mode 100644 index 0000000..43dc498 --- /dev/null +++ b/parts/res/drawable/ic_doze_brightness_high.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_doze_brightness_low.xml b/parts/res/drawable/ic_doze_brightness_low.xml new file mode 100644 index 0000000..ec13c12 --- /dev/null +++ b/parts/res/drawable/ic_doze_brightness_low.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_refresh_120.xml b/parts/res/drawable/ic_refresh_120.xml new file mode 100644 index 0000000..f81418b --- /dev/null +++ b/parts/res/drawable/ic_refresh_120.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_refresh_60.xml b/parts/res/drawable/ic_refresh_60.xml new file mode 100644 index 0000000..2c4a62f --- /dev/null +++ b/parts/res/drawable/ic_refresh_60.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_refresh_default.xml b/parts/res/drawable/ic_refresh_default.xml new file mode 100644 index 0000000..a4b7d3a --- /dev/null +++ b/parts/res/drawable/ic_refresh_default.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_scenes.xml b/parts/res/drawable/ic_scenes.xml new file mode 100644 index 0000000..1eee4f1 --- /dev/null +++ b/parts/res/drawable/ic_scenes.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_thermal_benchmark.xml b/parts/res/drawable/ic_thermal_benchmark.xml new file mode 100644 index 0000000..8b3f2c1 --- /dev/null +++ b/parts/res/drawable/ic_thermal_benchmark.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_browser.xml b/parts/res/drawable/ic_thermal_browser.xml new file mode 100644 index 0000000..73880a0 --- /dev/null +++ b/parts/res/drawable/ic_thermal_browser.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/parts/res/drawable/ic_thermal_camera.xml b/parts/res/drawable/ic_thermal_camera.xml new file mode 100644 index 0000000..642c082 --- /dev/null +++ b/parts/res/drawable/ic_thermal_camera.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_default.xml b/parts/res/drawable/ic_thermal_default.xml new file mode 100644 index 0000000..cc78c36 --- /dev/null +++ b/parts/res/drawable/ic_thermal_default.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_dialer.xml b/parts/res/drawable/ic_thermal_dialer.xml new file mode 100644 index 0000000..f87e39b --- /dev/null +++ b/parts/res/drawable/ic_thermal_dialer.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_gaming.xml b/parts/res/drawable/ic_thermal_gaming.xml new file mode 100644 index 0000000..29a13b8 --- /dev/null +++ b/parts/res/drawable/ic_thermal_gaming.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_streaming.xml b/parts/res/drawable/ic_thermal_streaming.xml new file mode 100644 index 0000000..e6e272b --- /dev/null +++ b/parts/res/drawable/ic_thermal_streaming.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/layout/refresh_layout.xml b/parts/res/layout/refresh_layout.xml new file mode 100644 index 0000000..6467efa --- /dev/null +++ b/parts/res/layout/refresh_layout.xml @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/parts/res/layout/refresh_list_item.xml b/parts/res/layout/refresh_list_item.xml new file mode 100644 index 0000000..e2ce15f --- /dev/null +++ b/parts/res/layout/refresh_list_item.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + diff --git a/parts/res/layout/thermal_layout.xml b/parts/res/layout/thermal_layout.xml new file mode 100644 index 0000000..3982a35 --- /dev/null +++ b/parts/res/layout/thermal_layout.xml @@ -0,0 +1,18 @@ + + + diff --git a/parts/res/layout/thermal_list_item.xml b/parts/res/layout/thermal_list_item.xml new file mode 100644 index 0000000..864b7d9 --- /dev/null +++ b/parts/res/layout/thermal_list_item.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + diff --git a/parts/res/raw/clear_speaker_sound.mp3 b/parts/res/raw/clear_speaker_sound.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a67b135160693d6d15611dc16fb301b6d27109d7 GIT binary patch literal 56654 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 + + + + @string/doze_brightness_low + @string/doze_brightness_high + @string/doze_brightness_adaptive + + + 0 + 1 + 2 + + diff --git a/parts/res/values/strings.xml b/parts/res/values/strings.xml new file mode 100644 index 0000000..56a1844 --- /dev/null +++ b/parts/res/values/strings.xml @@ -0,0 +1,49 @@ + + + + + + Thermal profiles + Adjust per-app thermal profiles for optimum performance + No optimization + Performance + Browser + Camera + Dialer + Gaming + Streaming + + + Per-app refresh rate + Set the maximum refresh rate for a specific application + Default + 60Hz + 120Hz + + + Low brightness + High brightness + Adaptive brightness + Doze brightness level + Brightness level of doze pulse screen (% of max allowed brightness) + + + 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. + + diff --git a/parts/res/xml/clear_speaker_settings.xml b/parts/res/xml/clear_speaker_settings.xml new file mode 100644 index 0000000..8716efb --- /dev/null +++ b/parts/res/xml/clear_speaker_settings.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/parts/res/xml/doze_settings.xml b/parts/res/xml/doze_settings.xml new file mode 100644 index 0000000..db6577a --- /dev/null +++ b/parts/res/xml/doze_settings.xml @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/parts/src/org/lineageos/settings/BootCompletedReceiver.java b/parts/src/org/lineageos/settings/BootCompletedReceiver.java new file mode 100644 index 0000000..4617b7d --- /dev/null +++ b/parts/src/org/lineageos/settings/BootCompletedReceiver.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * 2017-2019 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; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import org.lineageos.settings.doze.DozeUtils; +import org.lineageos.settings.thermal.ThermalUtils; +import org.lineageos.settings.refreshrate.RefreshUtils; + +public class BootCompletedReceiver extends BroadcastReceiver { + private static final boolean DEBUG = false; + private static final String TAG = "XiaomiParts"; + + @Override + public void onReceive(final Context context, Intent intent) { + if (DEBUG) + Log.d(TAG, "Received boot completed intent"); + DozeUtils.onBootCompleted(context); + ThermalUtils.startService(context); + RefreshUtils.startService(context); + } +} diff --git a/parts/src/org/lineageos/settings/doze/AodSensor.java b/parts/src/org/lineageos/settings/doze/AodSensor.java new file mode 100644 index 0000000..2216999 --- /dev/null +++ b/parts/src/org/lineageos/settings/doze/AodSensor.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 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.doze; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class AodSensor implements SensorEventListener { + private static final boolean DEBUG = false; + private static final String TAG = "AodSensor"; + + private SensorManager mSensorManager; + private Sensor mSensor; + private Context mContext; + private ExecutorService mExecutorService; + + public AodSensor(Context context) { + mContext = context; + mSensorManager = mContext.getSystemService(SensorManager.class); + mSensor = DozeUtils.getSensor(mSensorManager, "xiaomi.sensor.aod"); + mExecutorService = Executors.newSingleThreadExecutor(); + } + + private Future submit(Runnable runnable) { return mExecutorService.submit(runnable); } + + @Override + public void onSensorChanged(SensorEvent event) { + if (DEBUG) { + Log.d(TAG, "Got sensor event: " + event.values[0]); + } + + if (event.values[0] == 3 || event.values[0] == 5) { + DozeUtils.setDozeMode(DozeUtils.DOZE_MODE_LBM); + } else if (event.values[0] == 4) { + DozeUtils.setDozeMode(DozeUtils.DOZE_MODE_HBM); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + /* Empty */ + } + + protected void enable() { + if (DEBUG) { + Log.d(TAG, "Enabling"); + } + submit(() -> { + mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL); + }); + } + + protected void disable() { + if (DEBUG) { + Log.d(TAG, "Disabling"); + } + submit(() -> { mSensorManager.unregisterListener(this, mSensor); }); + } +} diff --git a/parts/src/org/lineageos/settings/doze/DozeService.java b/parts/src/org/lineageos/settings/doze/DozeService.java new file mode 100644 index 0000000..641ce02 --- /dev/null +++ b/parts/src/org/lineageos/settings/doze/DozeService.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * 2017-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.doze; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; +import android.util.Log; + +public class DozeService extends Service { + private static final String TAG = "DozeService"; + private static final boolean DEBUG = false; + + private AodSensor mAodSensor; + + @Override + public void onCreate() { + if (DEBUG) + Log.d(TAG, "Creating service"); + mAodSensor = new AodSensor(this); + + IntentFilter screenStateFilter = new IntentFilter(); + screenStateFilter.addAction(Intent.ACTION_SCREEN_ON); + screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF); + registerReceiver(mScreenStateReceiver, screenStateFilter); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) + Log.d(TAG, "Starting service"); + return START_STICKY; + } + + @Override + public void onDestroy() { + if (DEBUG) + Log.d(TAG, "Destroying service"); + super.onDestroy(); + this.unregisterReceiver(mScreenStateReceiver); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void onDisplayOn() { + if (DEBUG) + Log.d(TAG, "Display on"); + if (DozeUtils.isDozeAutoBrightnessEnabled(this)) { + mAodSensor.disable(); + } + } + + private void onDisplayOff() { + if (DEBUG) + Log.d(TAG, "Display off"); + if (DozeUtils.isDozeAutoBrightnessEnabled(this)) { + mAodSensor.enable(); + } + } + + private BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + onDisplayOn(); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + onDisplayOff(); + } + } + }; +} diff --git a/parts/src/org/lineageos/settings/doze/DozeSettingsActivity.java b/parts/src/org/lineageos/settings/doze/DozeSettingsActivity.java new file mode 100644 index 0000000..1657e39 --- /dev/null +++ b/parts/src/org/lineageos/settings/doze/DozeSettingsActivity.java @@ -0,0 +1,35 @@ +/* + * 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.doze; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; +import com.android.settingslib.widget.R; + +public class DozeSettingsActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_DOZE = "doze"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(R.id.content_frame, + new DozeSettingsFragment(), TAG_DOZE).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/doze/DozeSettingsFragment.java b/parts/src/org/lineageos/settings/doze/DozeSettingsFragment.java new file mode 100644 index 0000000..054b07c --- /dev/null +++ b/parts/src/org/lineageos/settings/doze/DozeSettingsFragment.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * 2017-2019 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.doze; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.widget.Switch; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceChangeListener; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragment; +import androidx.preference.SwitchPreference; + +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.OnMainSwitchChangeListener; + +import org.lineageos.settings.R; +import org.lineageos.settings.utils.FileUtils; + +public class DozeSettingsFragment extends PreferenceFragment + implements OnPreferenceChangeListener, OnMainSwitchChangeListener { + private MainSwitchPreference mSwitchBar; + + private SwitchPreference mAlwaysOnDisplayPreference; + private ListPreference mDozeBrightnessPreference; + + private Handler mHandler = new Handler(); + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.doze_settings); + + SharedPreferences prefs = + getActivity().getSharedPreferences("doze_settings", Activity.MODE_PRIVATE); + if (savedInstanceState == null && !prefs.getBoolean("first_help_shown", false)) { + showHelp(); + } + + boolean dozeEnabled = DozeUtils.isDozeEnabled(getActivity()); + + mSwitchBar = (MainSwitchPreference) findPreference(DozeUtils.DOZE_ENABLE); + mSwitchBar.addOnSwitchChangeListener(this); + mSwitchBar.setChecked(dozeEnabled); + + mAlwaysOnDisplayPreference = (SwitchPreference) findPreference(DozeUtils.ALWAYS_ON_DISPLAY); + mAlwaysOnDisplayPreference.setEnabled(dozeEnabled); + mAlwaysOnDisplayPreference.setChecked(DozeUtils.isAlwaysOnEnabled(getActivity())); + mAlwaysOnDisplayPreference.setOnPreferenceChangeListener(this); + + mDozeBrightnessPreference = (ListPreference) findPreference(DozeUtils.DOZE_BRIGHTNESS_KEY); + mDozeBrightnessPreference.setEnabled( + dozeEnabled && DozeUtils.isAlwaysOnEnabled(getActivity())); + mDozeBrightnessPreference.setOnPreferenceChangeListener(this); + + // Hide AOD and doze brightness if not supported and set all its dependents otherwise + if (!DozeUtils.alwaysOnDisplayAvailable(getActivity())) { + getPreferenceScreen().removePreference(mAlwaysOnDisplayPreference); + getPreferenceScreen().removePreference(mDozeBrightnessPreference); + } else { + if (!FileUtils.isFileWritable(DozeUtils.DOZE_MODE_PATH)) { + getPreferenceScreen().removePreference(mDozeBrightnessPreference); + } else { + DozeUtils.updateDozeBrightnessIcon(getContext(), mDozeBrightnessPreference); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (DozeUtils.ALWAYS_ON_DISPLAY.equals(preference.getKey())) { + DozeUtils.enableAlwaysOn(getActivity(), (Boolean) newValue); + if (!(Boolean) newValue) { + mDozeBrightnessPreference.setValue(DozeUtils.DOZE_BRIGHTNESS_LBM); + DozeUtils.setDozeMode(DozeUtils.DOZE_BRIGHTNESS_LBM); + } + mDozeBrightnessPreference.setEnabled((Boolean) newValue); + } else if (DozeUtils.DOZE_BRIGHTNESS_KEY.equals(preference.getKey())) { + if (!DozeUtils.DOZE_BRIGHTNESS_AUTO.equals((String) newValue)) { + DozeUtils.setDozeMode((String) newValue); + } + } + + mHandler.post(() -> { + DozeUtils.checkDozeService(getActivity()); + DozeUtils.updateDozeBrightnessIcon(getContext(), mDozeBrightnessPreference); + }); + + return true; + } + + @Override + public void onSwitchChanged(Switch switchView, boolean isChecked) { + DozeUtils.enableDoze(getActivity(), isChecked); + DozeUtils.checkDozeService(getActivity()); + + mSwitchBar.setChecked(isChecked); + + if (!isChecked) { + DozeUtils.enableAlwaysOn(getActivity(), false); + mAlwaysOnDisplayPreference.setChecked(false); + mDozeBrightnessPreference.setValue(DozeUtils.DOZE_BRIGHTNESS_LBM); + DozeUtils.updateDozeBrightnessIcon(getContext(), mDozeBrightnessPreference); + } + mAlwaysOnDisplayPreference.setEnabled(isChecked); + mDozeBrightnessPreference.setEnabled( + isChecked && DozeUtils.isAlwaysOnEnabled(getActivity())); + } + + public static class HelpDialogFragment extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.doze_settings_help_title) + .setMessage(R.string.doze_settings_help_text) + .setNegativeButton(R.string.dialog_ok, (dialog, which) -> dialog.cancel()) + .create(); + } + + @Override + public void onCancel(DialogInterface dialog) { + getActivity() + .getSharedPreferences("doze_settings", Activity.MODE_PRIVATE) + .edit() + .putBoolean("first_help_shown", true) + .commit(); + } + } + + private void showHelp() { + HelpDialogFragment fragment = new HelpDialogFragment(); + fragment.show(getFragmentManager(), "help_dialog"); + } +} diff --git a/parts/src/org/lineageos/settings/doze/DozeUtils.java b/parts/src/org/lineageos/settings/doze/DozeUtils.java new file mode 100644 index 0000000..a32414e --- /dev/null +++ b/parts/src/org/lineageos/settings/doze/DozeUtils.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * 2017-2019 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.doze; + +import static android.provider.Settings.Secure.DOZE_ALWAYS_ON; +import static android.provider.Settings.Secure.DOZE_ENABLED; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.display.AmbientDisplayConfiguration; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +import androidx.preference.ListPreference; +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.R; +import org.lineageos.settings.utils.FileUtils; + +public final class DozeUtils { + private static final String TAG = "DozeUtils"; + private static final boolean DEBUG = false; + + private static final String DOZE_INTENT = "com.android.systemui.doze.pulse"; + + protected static final String DOZE_ENABLE = "doze_enable"; + protected static final String ALWAYS_ON_DISPLAY = "always_on_display"; + protected static final String DOZE_BRIGHTNESS_KEY = "doze_brightness"; + + protected static final String DOZE_MODE_PATH = + "/sys/devices/platform/soc/soc:qcom,dsi-display-primary/doze_mode"; + protected static final String DOZE_MODE_HBM = "1"; + protected static final String DOZE_MODE_LBM = "0"; + + protected static final String DOZE_BRIGHTNESS_LBM = "0"; + protected static final String DOZE_BRIGHTNESS_HBM = "1"; + protected static final String DOZE_BRIGHTNESS_AUTO = "2"; + + public static void onBootCompleted(Context context) { + checkDozeService(context); + restoreDozeModes(context); + } + public static void startService(Context context) { + if (DEBUG) + Log.d(TAG, "Starting service"); + context.startServiceAsUser(new Intent(context, DozeService.class), UserHandle.CURRENT); + } + + protected static void stopService(Context context) { + if (DEBUG) + Log.d(TAG, "Stopping service"); + context.stopServiceAsUser(new Intent(context, DozeService.class), UserHandle.CURRENT); + } + + public static void checkDozeService(Context context) { + if (isDozeEnabled(context) + && (isAlwaysOnEnabled(context) || sensorsEnabled(context))) { + startService(context); + } else { + stopService(context); + } + } + + private static void restoreDozeModes(Context context) { + if (isAlwaysOnEnabled(context) && !isDozeAutoBrightnessEnabled(context)) { + setDozeMode(PreferenceManager.getDefaultSharedPreferences(context).getString( + DOZE_BRIGHTNESS_KEY, String.valueOf(DOZE_BRIGHTNESS_LBM))); + } + } + + protected static boolean enableDoze(Context context, boolean enable) { + return Settings.Secure.putInt(context.getContentResolver(), DOZE_ENABLED, enable ? 1 : 0); + } + + public static boolean isDozeEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), DOZE_ENABLED, 1) != 0; + } + + protected static boolean enableAlwaysOn(Context context, boolean enable) { + return Settings.Secure.putIntForUser(context.getContentResolver(), DOZE_ALWAYS_ON, + enable ? 1 : 0, UserHandle.USER_CURRENT); + } + + protected static boolean isAlwaysOnEnabled(Context context) { + final boolean enabledByDefault = context.getResources().getBoolean( + com.android.internal.R.bool.config_dozeAlwaysOnEnabled); + + return Settings.Secure.getIntForUser(context.getContentResolver(), DOZE_ALWAYS_ON, + alwaysOnDisplayAvailable(context) && enabledByDefault ? 1 : 0, + UserHandle.USER_CURRENT) + != 0; + } + + protected static boolean alwaysOnDisplayAvailable(Context context) { + return new AmbientDisplayConfiguration(context).alwaysOnAvailable(); + } + + protected static boolean setDozeMode(String value) { + return FileUtils.writeLine(DOZE_MODE_PATH, value); + } + + protected static boolean isDozeAutoBrightnessEnabled(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getString(DOZE_BRIGHTNESS_KEY, DOZE_BRIGHTNESS_LBM) + .equals(DOZE_BRIGHTNESS_AUTO); + } + + public static boolean sensorsEnabled(Context context) { + return isDozeAutoBrightnessEnabled(context); + } + + protected static Sensor getSensor(SensorManager sm, String type) { + for (Sensor sensor : sm.getSensorList(Sensor.TYPE_ALL)) { + if (type.equals(sensor.getStringType())) { + return sensor; + } + } + return null; + } + + protected static void updateDozeBrightnessIcon(Context context, ListPreference preference) { + switch (PreferenceManager.getDefaultSharedPreferences(context).getString( + DOZE_BRIGHTNESS_KEY, DOZE_BRIGHTNESS_LBM)) { + case DozeUtils.DOZE_BRIGHTNESS_LBM: + preference.setIcon(R.drawable.ic_doze_brightness_low); + break; + case DozeUtils.DOZE_BRIGHTNESS_HBM: + preference.setIcon(R.drawable.ic_doze_brightness_high); + break; + case DozeUtils.DOZE_BRIGHTNESS_AUTO: + preference.setIcon(R.drawable.ic_doze_brightness_auto); + break; + } + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java new file mode 100644 index 0000000..2ecf2a4 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020-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.refreshrate; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; +import com.android.settingslib.widget.R; + +public class RefreshActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_REFRESH = "refresh"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(R.id.content_frame, + new RefreshSettingsFragment(), TAG_REFRESH).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshService.java b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java new file mode 100644 index 0000000..660bdd0 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 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.refreshrate; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ActivityTaskManager.RootTaskInfo; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; +import android.os.RemoteException; + +public class RefreshService extends Service { + + private static final String TAG = "RefreshService"; + private static final boolean DEBUG = true; + + private String mPreviousApp; + private RefreshUtils mRefreshUtils; + private IActivityTaskManager mActivityTaskManager; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mPreviousApp = ""; + } + }; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "Creating service"); + try { + mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager.registerTaskStackListener(mTaskListener); + } catch (RemoteException e) { + // Do nothing + } + mRefreshUtils = new RefreshUtils(this); + registerReceiver(); + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) Log.d(TAG, "Starting service"); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void registerReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + this.registerReceiver(mIntentReceiver, filter); + } + + private final TaskStackListener mTaskListener = new TaskStackListener() { + @Override + public void onTaskStackChanged() { + try { + final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo(); + if (info == null || info.topActivity == null) { + return; + } + String foregroundApp = info.topActivity.getPackageName(); + if (!mRefreshUtils.isAppInList) { + mRefreshUtils.getOldRate(); + } + if (!foregroundApp.equals(mPreviousApp)) { + mRefreshUtils.setRefreshRate(foregroundApp); + mPreviousApp = foregroundApp; + } + } catch (Exception e) {} + } + }; + } \ No newline at end of file diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java new file mode 100644 index 0000000..04a4063 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java @@ -0,0 +1,419 @@ +/** + * Copyright (C) 2020 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.refreshrate; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceFragment; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.settingslib.applications.ApplicationsState; + +import org.lineageos.settings.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RefreshSettingsFragment extends PreferenceFragment + implements ApplicationsState.Callbacks { + + private AllPackagesAdapter mAllPackagesAdapter; + private ApplicationsState mApplicationsState; + private ApplicationsState.Session mSession; + private ActivityFilter mActivityFilter; + private Map mEntryMap = + new HashMap(); + + private RefreshUtils mRefreshUtils; + private RecyclerView mAppsRecyclerView; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mApplicationsState.newSession(this); + mSession.onResume(); + mActivityFilter = new ActivityFilter(getActivity().getPackageManager()); + + mAllPackagesAdapter = new AllPackagesAdapter(getActivity()); + + mRefreshUtils = new RefreshUtils(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.refresh_layout, container, false); + } + + @Override + public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAppsRecyclerView = view.findViewById(R.id.refresh_rv_view); + mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + mAppsRecyclerView.setAdapter(mAllPackagesAdapter); + } + + + @Override + public void onResume() { + super.onResume(); + getActivity().setTitle(getResources().getString(R.string.refresh_title)); + rebuild(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mSession.onPause(); + mSession.onDestroy(); + } + + @Override + public void onPackageListChanged() { + mActivityFilter.updateLauncherInfoList(); + rebuild(); + } + + @Override + public void onRebuildComplete(ArrayList entries) { + if (entries != null) { + handleAppEntries(entries); + mAllPackagesAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onLoadEntriesCompleted() { + rebuild(); + } + + @Override + public void onAllSizesComputed() { + } + + @Override + public void onLauncherInfoChanged() { + } + + @Override + public void onPackageIconChanged() { + } + + @Override + public void onPackageSizeChanged(String packageName) { + } + + @Override + public void onRunningStateChanged(boolean running) { + } + + private void handleAppEntries(List entries) { + final ArrayList sections = new ArrayList(); + final ArrayList positions = new ArrayList(); + final PackageManager pm = getActivity().getPackageManager(); + String lastSectionIndex = null; + int offset = 0; + + for (int i = 0; i < entries.size(); i++) { + final ApplicationInfo info = entries.get(i).info; + final String label = (String) info.loadLabel(pm); + final String sectionIndex; + + if (!info.enabled) { + sectionIndex = "--"; // XXX + } else if (TextUtils.isEmpty(label)) { + sectionIndex = ""; + } else { + sectionIndex = label.substring(0, 1).toUpperCase(); + } + + if (lastSectionIndex == null || + !TextUtils.equals(sectionIndex, lastSectionIndex)) { + sections.add(sectionIndex); + positions.add(offset); + lastSectionIndex = sectionIndex; + } + + offset++; + } + + mAllPackagesAdapter.setEntries(entries, sections, positions); + mEntryMap.clear(); + for (ApplicationsState.AppEntry e : entries) { + mEntryMap.put(e.info.packageName, e); + } + } + + private void rebuild() { + mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR); + } + + private int getStateDrawable(int state) { + switch (state) { + case RefreshUtils.STATE_STANDARD: + return R.drawable.ic_refresh_60; + case RefreshUtils.STATE_EXTREME: + return R.drawable.ic_refresh_120; + case RefreshUtils.STATE_DEFAULT: + default: + return R.drawable.ic_refresh_default; + } + } + + private class ViewHolder extends RecyclerView.ViewHolder { + private TextView title; + private Spinner mode; + private ImageView icon; + private View rootView; + private ImageView stateIcon; + + private ViewHolder(View view) { + super(view); + this.title = view.findViewById(R.id.app_name); + this.mode = view.findViewById(R.id.app_mode); + this.icon = view.findViewById(R.id.app_icon); + this.stateIcon = view.findViewById(R.id.state); + this.rootView = view; + + view.setTag(this); + } + } + + private class ModeAdapter extends BaseAdapter { + + private final LayoutInflater inflater; + private final int[] items = { + R.string.refresh_default, + R.string.refresh_standard, + R.string.refresh_extreme + }; + + private ModeAdapter(Context context) { + inflater = LayoutInflater.from(context); + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Object getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView view; + if (convertView != null) { + view = (TextView) convertView; + } else { + view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item, + parent, false); + } + + view.setText(items[position]); + view.setTextSize(14f); + + return view; + } + } + + private class AllPackagesAdapter extends RecyclerView.Adapter + implements AdapterView.OnItemSelectedListener, SectionIndexer { + + private List mEntries = new ArrayList<>(); + private String[] mSections; + private int[] mPositions; + + public AllPackagesAdapter(Context context) { + mActivityFilter = new ActivityFilter(context.getPackageManager()); + } + + @Override + public int getItemCount() { + return mEntries.size(); + } + + @Override + public long getItemId(int position) { + return mEntries.get(position).id; + } +@NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.refresh_list_item, parent, false)); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Context context = holder.itemView.getContext(); + + ApplicationsState.AppEntry entry = mEntries.get(position); + + if (entry == null) { + return; + } + holder.mode.setAdapter(new ModeAdapter(context)); + holder.mode.setOnItemSelectedListener(this); + holder.title.setText(entry.label); + holder.title.setOnClickListener(v -> holder.mode.performClick()); + mApplicationsState.ensureIcon(entry); + holder.icon.setImageDrawable(entry.icon); + int packageState = mRefreshUtils.getStateForPackage(entry.info.packageName); + holder.mode.setSelection(packageState, false); + holder.mode.setTag(entry); + holder.stateIcon.setImageResource(getStateDrawable(packageState)); + } + + private void setEntries(List entries, + List sections, List positions) { + mEntries = entries; + mSections = sections.toArray(new String[sections.size()]); + mPositions = new int[positions.size()]; + for (int i = 0; i < positions.size(); i++) { + mPositions[i] = positions.get(i); + } + notifyDataSetChanged(); + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag(); + + int currentState = mRefreshUtils.getStateForPackage(entry.info.packageName); + if (currentState != position) { + mRefreshUtils.writePackage(entry.info.packageName, position); + notifyDataSetChanged(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + @Override + public int getPositionForSection(int section) { + if (section < 0 || section >= mSections.length) { + return -1; + } + + return mPositions[section]; + } + + @Override + public int getSectionForPosition(int position) { + if (position < 0 || position >= getItemCount()) { + return -1; + } + + final int index = Arrays.binarySearch(mPositions, position); + + /* + * Consider this example: section positions are 0, 3, 5; the supplied + * position is 4. The section corresponding to position 4 starts at + * position 3, so the expected return value is 1. Binary search will not + * find 4 in the array and thus will return -insertPosition-1, i.e. -3. + * To get from that number to the expected value of 1 we need to negate + * and subtract 2. + */ + return index >= 0 ? index : -index - 2; + } + + @Override + public Object[] getSections() { + return mSections; + } + } + + private class ActivityFilter implements ApplicationsState.AppFilter { + + private final PackageManager mPackageManager; + private final List mLauncherResolveInfoList = new ArrayList(); + + private ActivityFilter(PackageManager packageManager) { + this.mPackageManager = packageManager; + + updateLauncherInfoList(); + } + + public void updateLauncherInfoList() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfoList = mPackageManager.queryIntentActivities(i, 0); + + synchronized (mLauncherResolveInfoList) { + mLauncherResolveInfoList.clear(); + for (ResolveInfo ri : resolveInfoList) { + mLauncherResolveInfoList.add(ri.activityInfo.packageName); + } + } + } + + @Override + public void init() { + } + + @Override + public boolean filterApp(ApplicationsState.AppEntry entry) { + boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName); + if (show) { + synchronized (mLauncherResolveInfoList) { + show = mLauncherResolveInfoList.contains(entry.info.packageName); + } + } + return show; + } + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java new file mode 100644 index 0000000..d5b7acf --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 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.refreshrate; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.UserHandle; +import android.view.Display; + +import android.provider.Settings; +import androidx.preference.PreferenceManager; + +public final class RefreshUtils { + + private static final String REFRESH_CONTROL = "refresh_control"; + + private static float defaultMaxRate; + private static float defaultMinRate; + private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate"; + private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate"; + private Context mContext; + protected static boolean isAppInList = false; + + protected static final int STATE_DEFAULT = 0; + protected static final int STATE_STANDARD = 1; + protected static final int STATE_EXTREME = 2; + + private static final float REFRESH_STATE_DEFAULT = 120f; + private static final float REFRESH_STATE_STANDARD = 60f; + private static final float REFRESH_STATE_EXTREME = 120f; + + private static final String REFRESH_STANDARD = "refresh.standard="; + private static final String REFRESH_EXTREME = "refresh.extreme="; + + private SharedPreferences mSharedPrefs; + + protected RefreshUtils(Context context) { + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + mContext = context; + } + + public static void startService(Context context) { + context.startServiceAsUser(new Intent(context, RefreshService.class), + UserHandle.CURRENT); + } + + private void writeValue(String profiles) { + mSharedPrefs.edit().putString(REFRESH_CONTROL, profiles).apply(); + } + + protected void getOldRate(){ + defaultMaxRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, REFRESH_STATE_DEFAULT); + defaultMinRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, REFRESH_STATE_DEFAULT); + } + + + private String getValue() { + String value = mSharedPrefs.getString(REFRESH_CONTROL, null); + + if (value == null || value.isEmpty()) { + value = REFRESH_STANDARD + ":" + REFRESH_EXTREME; + writeValue(value); + } + return value; + } + + protected void writePackage(String packageName, int mode) { + String value = getValue(); + value = value.replace(packageName + ",", ""); + String[] modes = value.split(":"); + String finalString; + + switch (mode) { + case STATE_STANDARD: + modes[0] = modes[0] + packageName + ","; + break; + case STATE_EXTREME: + modes[1] = modes[1] + packageName + ","; + break; + } + + finalString = modes[0] + ":" + modes[1]; + + writeValue(finalString); + } + + protected int getStateForPackage(String packageName) { + String value = getValue(); + String[] modes = value.split(":"); + int state = STATE_DEFAULT; + if (modes[0].contains(packageName + ",")) { + state = STATE_STANDARD; + } else if (modes[1].contains(packageName + ",")) { + state = STATE_EXTREME; + } + return state; + } + + protected void setRefreshRate(String packageName) { + String value = getValue(); + String modes[]; + float maxrate = defaultMaxRate; + float minrate = defaultMinRate; + isAppInList = false; + + if (value != null) { + modes = value.split(":"); + + if (modes[0].contains(packageName + ",")) { + maxrate = REFRESH_STATE_STANDARD; + if ( minrate > maxrate){ + minrate = maxrate; + } + isAppInList = true; + } else if (modes[1].contains(packageName + ",")) { + maxrate = REFRESH_STATE_EXTREME; + if ( minrate > maxrate){ + minrate = maxrate; + } + isAppInList = true; + } + } + Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, minrate); + Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, maxrate); + } +} diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java new file mode 100644 index 0000000..d1e74a4 --- /dev/null +++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 Paranoid Android + * + * 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.speaker; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; +import com.android.settingslib.widget.R; + +public class ClearSpeakerActivity extends CollapsingToolbarBaseActivity { + + private static final String TAG_CLEARSPEAKER = "clearspeaker"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(R.id.content_frame, + new ClearSpeakerFragment(), TAG_CLEARSPEAKER).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java new file mode 100644 index 0000000..4dad735 --- /dev/null +++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Paranoid Android + * + * 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.speaker; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioManager; +import android.media.AudioAttributes; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import androidx.preference.Preference; +import androidx.preference.PreferenceFragment; +import androidx.preference.SwitchPreference; + +import org.lineageos.settings.R; + +import java.io.IOException; + +public class ClearSpeakerFragment extends PreferenceFragment implements + Preference.OnPreferenceChangeListener { + + private static final String TAG = ClearSpeakerFragment.class.getSimpleName(); + + private static final String PREF_CLEAR_SPEAKER = "clear_speaker_pref"; + + private AudioManager mAudioManager; + private Handler mHandler; + private MediaPlayer mMediaPlayer; + private SwitchPreference mClearSpeakerPref; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.clear_speaker_settings); + + mClearSpeakerPref = (SwitchPreference) findPreference(PREF_CLEAR_SPEAKER); + mClearSpeakerPref.setOnPreferenceChangeListener(this); + + mHandler = new Handler(); + mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mClearSpeakerPref) { + boolean value = (Boolean) newValue; + if (value) { + if (startPlaying()) { + mHandler.removeCallbacksAndMessages(null); + mHandler.postDelayed(() -> { + stopPlaying(); + }, 30000); + return true; + } + } + } + return false; + } + + @Override + public void onStop() { + super.onStop(); + stopPlaying(); + } + + public boolean startPlaying() { + mAudioManager.setParameters("status_earpiece_clean=on"); + mMediaPlayer = new MediaPlayer(); + getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mMediaPlayer.setLooping(true); + try { + AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.clear_speaker_sound); + try { + mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); + } finally { + file.close(); + } + mClearSpeakerPref.setEnabled(false); + mMediaPlayer.setVolume(1.0f, 1.0f); + mMediaPlayer.prepare(); + mMediaPlayer.start(); + } catch (IOException ioe) { + Log.e(TAG, "Failed to play speaker clean sound!", ioe); + return false; + } + return true; + } + + public void stopPlaying() { + if (mMediaPlayer != null) { + if (mMediaPlayer.isPlaying()) { + mMediaPlayer.stop(); + mMediaPlayer.reset(); + mMediaPlayer.release(); + mMediaPlayer=null; + } + } + mAudioManager.setParameters("status_earpiece_clean=off"); + mClearSpeakerPref.setEnabled(true); + mClearSpeakerPref.setChecked(false); + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalActivity.java b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java new file mode 100644 index 0000000..c583a20 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020-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.thermal; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class ThermalActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_THERMAL = "thermal"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new ThermalSettingsFragment(), TAG_THERMAL).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalService.java b/parts/src/org/lineageos/settings/thermal/ThermalService.java new file mode 100644 index 0000000..5ec4610 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalService.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 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.thermal; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.TaskStackListener; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +public class ThermalService extends Service { + + private static final String TAG = "ThermalService"; + private static final boolean DEBUG = false; + + private String mPreviousApp; + private ThermalUtils mThermalUtils; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mPreviousApp = ""; + mThermalUtils.setDefaultThermalProfile(); + } + }; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "Creating service"); + try { + ActivityTaskManager.getService().registerTaskStackListener(mTaskListener); + } catch (RemoteException e) { + // Do nothing + } + mThermalUtils = new ThermalUtils(this); + registerReceiver(); + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) Log.d(TAG, "Starting service"); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void registerReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + this.registerReceiver(mIntentReceiver, filter); + } + + private final TaskStackListener mTaskListener = new TaskStackListener() { + @Override + public void onTaskStackChanged() { + try { + final ActivityTaskManager.RootTaskInfo focusedTask = + ActivityTaskManager.getService().getFocusedRootTaskInfo(); + if (focusedTask != null && focusedTask.topActivity != null) { + ComponentName taskComponentName = focusedTask.topActivity; + String foregroundApp = taskComponentName.getPackageName(); + if (!foregroundApp.equals(mPreviousApp)) { + mThermalUtils.setThermalProfile(foregroundApp); + mPreviousApp = foregroundApp; + } + } + } catch (Exception e) {} + } + }; +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java new file mode 100644 index 0000000..8123f05 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java @@ -0,0 +1,428 @@ +/** + * Copyright (C) 2020 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.thermal; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceFragment; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.settingslib.applications.ApplicationsState; + +import org.lineageos.settings.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ThermalSettingsFragment extends PreferenceFragment + implements ApplicationsState.Callbacks { + + private AllPackagesAdapter mAllPackagesAdapter; + private ApplicationsState mApplicationsState; + private ApplicationsState.Session mSession; + private ActivityFilter mActivityFilter; + private Map mEntryMap = + new HashMap(); + + private ThermalUtils mThermalUtils; + private RecyclerView mAppsRecyclerView; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mApplicationsState.newSession(this); + mSession.onResume(); + mActivityFilter = new ActivityFilter(getActivity().getPackageManager()); + + mAllPackagesAdapter = new AllPackagesAdapter(getActivity()); + + mThermalUtils = new ThermalUtils(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.thermal_layout, container, false); + } + + @Override + public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAppsRecyclerView = view.findViewById(R.id.thermal_rv_view); + mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + mAppsRecyclerView.setAdapter(mAllPackagesAdapter); + } + + + @Override + public void onResume() { + super.onResume(); + rebuild(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mSession.onPause(); + mSession.onDestroy(); + } + + @Override + public void onPackageListChanged() { + mActivityFilter.updateLauncherInfoList(); + rebuild(); + } + + @Override + public void onRebuildComplete(ArrayList entries) { + if (entries != null) { + handleAppEntries(entries); + mAllPackagesAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onLoadEntriesCompleted() { + rebuild(); + } + + @Override + public void onAllSizesComputed() { + } + + @Override + public void onLauncherInfoChanged() { + } + + @Override + public void onPackageIconChanged() { + } + + @Override + public void onPackageSizeChanged(String packageName) { + } + + @Override + public void onRunningStateChanged(boolean running) { + } + + private void handleAppEntries(List entries) { + final ArrayList sections = new ArrayList(); + final ArrayList positions = new ArrayList(); + final PackageManager pm = getActivity().getPackageManager(); + String lastSectionIndex = null; + int offset = 0; + + for (int i = 0; i < entries.size(); i++) { + final ApplicationInfo info = entries.get(i).info; + final String label = (String) info.loadLabel(pm); + final String sectionIndex; + + if (!info.enabled) { + sectionIndex = "--"; // XXX + } else if (TextUtils.isEmpty(label)) { + sectionIndex = ""; + } else { + sectionIndex = label.substring(0, 1).toUpperCase(); + } + + if (lastSectionIndex == null || + !TextUtils.equals(sectionIndex, lastSectionIndex)) { + sections.add(sectionIndex); + positions.add(offset); + lastSectionIndex = sectionIndex; + } + + offset++; + } + + mAllPackagesAdapter.setEntries(entries, sections, positions); + mEntryMap.clear(); + for (ApplicationsState.AppEntry e : entries) { + mEntryMap.put(e.info.packageName, e); + } + } + + private void rebuild() { + mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR); + } + + private int getStateDrawable(int state) { + switch (state) { + case ThermalUtils.STATE_BENCHMARK: + return R.drawable.ic_thermal_benchmark; + case ThermalUtils.STATE_BROWSER: + return R.drawable.ic_thermal_browser; + case ThermalUtils.STATE_CAMERA: + return R.drawable.ic_thermal_camera; + case ThermalUtils.STATE_DIALER: + return R.drawable.ic_thermal_dialer; + case ThermalUtils.STATE_GAMING: + return R.drawable.ic_thermal_gaming; + case ThermalUtils.STATE_STREAMING: + return R.drawable.ic_thermal_streaming; + case ThermalUtils.STATE_DEFAULT: + default: + return R.drawable.ic_thermal_default; + } + } + + private class ViewHolder extends RecyclerView.ViewHolder { + private TextView title; + private Spinner mode; + private ImageView icon; + private View rootView; + private ImageView stateIcon; + + private ViewHolder(View view) { + super(view); + this.title = view.findViewById(R.id.app_name); + this.mode = view.findViewById(R.id.app_mode); + this.icon = view.findViewById(R.id.app_icon); + this.stateIcon = view.findViewById(R.id.state); + this.rootView = view; + + view.setTag(this); + } + } + + private class ModeAdapter extends BaseAdapter { + + private final LayoutInflater inflater; + private final int[] items = { + R.string.thermal_default, + R.string.thermal_benchmark, + R.string.thermal_browser, + R.string.thermal_camera, + R.string.thermal_dialer, + R.string.thermal_gaming, + R.string.thermal_streaming + }; + + private ModeAdapter(Context context) { + inflater = LayoutInflater.from(context); + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Object getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView view; + if (convertView != null) { + view = (TextView) convertView; + } else { + view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item, + parent, false); + } + + view.setText(items[position]); + view.setTextSize(14f); + return view; + } + } + + private class AllPackagesAdapter extends RecyclerView.Adapter + implements AdapterView.OnItemSelectedListener, SectionIndexer { + + private List mEntries = new ArrayList<>(); + private String[] mSections; + private int[] mPositions; + + public AllPackagesAdapter(Context context) { + mActivityFilter = new ActivityFilter(context.getPackageManager()); + } + + @Override + public int getItemCount() { + return mEntries.size(); + } + + @Override + public long getItemId(int position) { + return mEntries.get(position).id; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.thermal_list_item, parent, false)); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Context context = holder.itemView.getContext(); + ApplicationsState.AppEntry entry = mEntries.get(position); + if (entry == null) { + return; + } + + holder.mode.setAdapter(new ModeAdapter(context)); + holder.mode.setOnItemSelectedListener(this); + holder.title.setText(entry.label); + holder.title.setOnClickListener(v -> holder.mode.performClick()); + mApplicationsState.ensureIcon(entry); + holder.icon.setImageDrawable(entry.icon); + int packageState = mThermalUtils.getStateForPackage(entry.info.packageName); + holder.mode.setSelection(packageState, false); + holder.mode.setTag(entry); + holder.stateIcon.setImageResource(getStateDrawable(packageState)); + } + + private void setEntries(List entries, + List sections, List positions) { + mEntries = entries; + mSections = sections.toArray(new String[sections.size()]); + mPositions = new int[positions.size()]; + for (int i = 0; i < positions.size(); i++) { + mPositions[i] = positions.get(i); + } + notifyDataSetChanged(); + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag(); + int currentState = mThermalUtils.getStateForPackage(entry.info.packageName); + if (currentState != position) { + mThermalUtils.writePackage(entry.info.packageName, position); + notifyDataSetChanged(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + @Override + public int getPositionForSection(int section) { + if (section < 0 || section >= mSections.length) { + return -1; + } + + return mPositions[section]; + } + + @Override + public int getSectionForPosition(int position) { + if (position < 0 || position >= getItemCount()) { + return -1; + } + + final int index = Arrays.binarySearch(mPositions, position); + + /* + * Consider this example: section positions are 0, 3, 5; the supplied + * position is 4. The section corresponding to position 4 starts at + * position 3, so the expected return value is 1. Binary search will not + * find 4 in the array and thus will return -insertPosition-1, i.e. -3. + * To get from that number to the expected value of 1 we need to negate + * and subtract 2. + */ + return index >= 0 ? index : -index - 2; + } + + @Override + public Object[] getSections() { + return mSections; + } + } + + private class ActivityFilter implements ApplicationsState.AppFilter { + + private final PackageManager mPackageManager; + private final List mLauncherResolveInfoList = new ArrayList(); + + private ActivityFilter(PackageManager packageManager) { + this.mPackageManager = packageManager; + + updateLauncherInfoList(); + } + + public void updateLauncherInfoList() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfoList = mPackageManager.queryIntentActivities(i, 0); + + synchronized (mLauncherResolveInfoList) { + mLauncherResolveInfoList.clear(); + for (ResolveInfo ri : resolveInfoList) { + mLauncherResolveInfoList.add(ri.activityInfo.packageName); + } + } + } + + @Override + public void init() { + } + + @Override + public boolean filterApp(ApplicationsState.AppEntry entry) { + boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName); + if (show) { + synchronized (mLauncherResolveInfoList) { + show = mLauncherResolveInfoList.contains(entry.info.packageName); + } + } + return show; + } + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalUtils.java b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java new file mode 100644 index 0000000..cf71fbe --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 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.thermal; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.UserHandle; + +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.utils.FileUtils; + +public final class ThermalUtils { + + private static final String THERMAL_CONTROL = "thermal_control"; + + protected static final int STATE_DEFAULT = 0; + protected static final int STATE_BENCHMARK = 1; + protected static final int STATE_BROWSER = 2; + protected static final int STATE_CAMERA = 3; + protected static final int STATE_DIALER = 4; + protected static final int STATE_GAMING = 5; + protected static final int STATE_STREAMING = 6; + + private static final String THERMAL_STATE_DEFAULT = "0"; + private static final String THERMAL_STATE_BENCHMARK = "10"; + private static final String THERMAL_STATE_BROWSER = "11"; + private static final String THERMAL_STATE_CAMERA = "12"; + private static final String THERMAL_STATE_DIALER = "8"; + private static final String THERMAL_STATE_GAMING = "9"; + private static final String THERMAL_STATE_STREAMING = "14"; + + private static final String THERMAL_BENCHMARK = "thermal.benchmark="; + private static final String THERMAL_BROWSER = "thermal.browser="; + private static final String THERMAL_CAMERA = "thermal.camera="; + private static final String THERMAL_DIALER = "thermal.dialer="; + private static final String THERMAL_GAMING = "thermal.gaming="; + private static final String THERMAL_STREAMING = "thermal.streaming="; + + private static final String THERMAL_SCONFIG = "/sys/class/thermal/thermal_message/sconfig"; + + private SharedPreferences mSharedPrefs; + + protected ThermalUtils(Context context) { + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public static void startService(Context context) { + context.startServiceAsUser(new Intent(context, ThermalService.class), + UserHandle.CURRENT); + } + + private void writeValue(String profiles) { + mSharedPrefs.edit().putString(THERMAL_CONTROL, profiles).apply(); + } + + private String getValue() { + String value = mSharedPrefs.getString(THERMAL_CONTROL, null); + + if (value == null || value.isEmpty()) { + value = THERMAL_BENCHMARK + ":" + THERMAL_BROWSER + ":" + THERMAL_CAMERA + ":" + + THERMAL_DIALER + ":" + THERMAL_GAMING + ":" + THERMAL_STREAMING; + writeValue(value); + } + return value; + } + + protected void writePackage(String packageName, int mode) { + String value = getValue(); + value = value.replace(packageName + ",", ""); + String[] modes = value.split(":"); + String finalString; + + switch (mode) { + case STATE_BENCHMARK: + modes[0] = modes[0] + packageName + ","; + break; + case STATE_BROWSER: + modes[1] = modes[1] + packageName + ","; + break; + case STATE_CAMERA: + modes[2] = modes[2] + packageName + ","; + break; + case STATE_DIALER: + modes[3] = modes[3] + packageName + ","; + break; + case STATE_GAMING: + modes[4] = modes[4] + packageName + ","; + break; + case STATE_STREAMING: + modes[5] = modes[5] + packageName + ","; + break; + } + + finalString = modes[0] + ":" + modes[1] + ":" + modes[2] + ":" + modes[3] + ":" + + modes[4] + ":" + modes[5]; + + writeValue(finalString); + } + + protected int getStateForPackage(String packageName) { + String value = getValue(); + String[] modes = value.split(":"); + int state = STATE_DEFAULT; + if (modes[0].contains(packageName + ",")) { + state = STATE_BENCHMARK; + } else if (modes[1].contains(packageName + ",")) { + state = STATE_BROWSER; + } else if (modes[2].contains(packageName + ",")) { + state = STATE_CAMERA; + } else if (modes[3].contains(packageName + ",")) { + state = STATE_DIALER; + } else if (modes[4].contains(packageName + ",")) { + state = STATE_GAMING; + } else if (modes[5].contains(packageName + ",")) { + state = STATE_STREAMING; + } + + return state; + } + + protected void setDefaultThermalProfile() { + FileUtils.writeLine(THERMAL_SCONFIG, THERMAL_STATE_DEFAULT); + } + + protected void setThermalProfile(String packageName) { + String value = getValue(); + String modes[]; + String state = THERMAL_STATE_DEFAULT; + + if (value != null) { + modes = value.split(":"); + + if (modes[0].contains(packageName + ",")) { + state = THERMAL_STATE_BENCHMARK; + } else if (modes[1].contains(packageName + ",")) { + state = THERMAL_STATE_BROWSER; + } else if (modes[2].contains(packageName + ",")) { + state = THERMAL_STATE_CAMERA; + } else if (modes[3].contains(packageName + ",")) { + state = THERMAL_STATE_DIALER; + } else if (modes[4].contains(packageName + ",")) { + state = THERMAL_STATE_GAMING; + } else if (modes[5].contains(packageName + ",")) { + state = THERMAL_STATE_STREAMING; + } + } + FileUtils.writeLine(THERMAL_SCONFIG, state); + } +} diff --git a/parts/src/org/lineageos/settings/utils/FileUtils.java b/parts/src/org/lineageos/settings/utils/FileUtils.java new file mode 100644 index 0000000..00028ff --- /dev/null +++ b/parts/src/org/lineageos/settings/utils/FileUtils.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.utils; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public final class FileUtils { + private static final String TAG = "FileUtils"; + + private FileUtils() { + // This class is not supposed to be instantiated + } + + /** + * Reads the first line of text from the given file. + * Reference {@link BufferedReader#readLine()} for clarification on what a line is + * + * @return the read line contents, or null on failure + */ + public static String readOneLine(String fileName) { + String line = null; + BufferedReader reader = null; + + try { + reader = new BufferedReader(new FileReader(fileName), 512); + line = reader.readLine(); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for reading", e); + } catch (IOException e) { + Log.e(TAG, "Could not read from file " + fileName, e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return line; + } + + /** + * Writes the given value into the given file + * + * @return true on success, false on failure + */ + public static boolean writeLine(String fileName, String value) { + BufferedWriter writer = null; + + try { + writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(value); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for writing", e); + return false; + } catch (IOException e) { + Log.e(TAG, "Could not write to file " + fileName, e); + return false; + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return true; + } + + /** + * Checks whether the given file exists + * + * @return true if exists, false if not + */ + public static boolean fileExists(String fileName) { + final File file = new File(fileName); + return file.exists(); + } + + /** + * Checks whether the given file is readable + * + * @return true if readable, false if not + */ + public static boolean isFileReadable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canRead(); + } + + /** + * Checks whether the given file is writable + * + * @return true if writable, false if not + */ + public static boolean isFileWritable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canWrite(); + } + + /** + * Deletes an existing file + * + * @return true if the delete was successful, false if not + */ + public static boolean delete(String fileName) { + final File file = new File(fileName); + boolean ok = false; + try { + ok = file.delete(); + } catch (SecurityException e) { + Log.w(TAG, "SecurityException trying to delete " + fileName, e); + } + return ok; + } + + /** + * Renames an existing file + * + * @return true if the rename was successful, false if not + */ + public static boolean rename(String srcPath, String dstPath) { + final File srcFile = new File(srcPath); + final File dstFile = new File(dstPath); + boolean ok = false; + try { + ok = srcFile.renameTo(dstFile); + } catch (SecurityException e) { + Log.w(TAG, "SecurityException trying to rename " + srcPath + " to " + dstPath, e); + } catch (NullPointerException e) { + Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e); + } + return ok; + } +}