├── Android.bp ├── AndroidManifest.xml ├── README.org ├── res ├── drawable │ ├── ic_dc_dimming_off.xml │ ├── ic_dc_dimming_on.xml │ ├── ic_fps.xml │ └── ic_hbm.xml ├── layout │ ├── displayfeatures.xml │ └── preference_widget_switch_compat.xml ├── values-es-rES │ └── strings.xml ├── values-vi │ └── strings.xml ├── values │ ├── config.xml │ ├── strings.xml │ └── symbols.xml └── xml │ └── displayfeatures_settings.xml └── src └── com └── android └── displayfeatures ├── BootCompletedReceiver.java ├── SettingsSearchIndexablesProvider.java ├── display ├── DisplayFeaturesActivity.java ├── DisplayFeaturesConfig.java ├── DisplayFeaturesDcDimTileService.java ├── DisplayFeaturesFpsService.java ├── DisplayFeaturesFpsTileService.java ├── DisplayFeaturesFragment.java ├── DisplayFeaturesHbmService.java └── DisplayFeaturesHbmTileService.java └── utils └── FileUtils.java /Android.bp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 CannedShroud 3 | // Copyright (C) 2023 cyberknight777 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | 8 | android_app { 9 | name: "DisplayFeatures", 10 | defaults: [ 11 | "SettingsLibDefaults", 12 | ], 13 | 14 | srcs: ["src/**/*.java"], 15 | resource_dirs: ["res"], 16 | certificate: "platform", 17 | platform_apis: true, 18 | system_ext_specific: true, 19 | privileged: true, 20 | 21 | static_libs: [ 22 | "androidx.core_core", 23 | "androidx.preference_preference", 24 | "SettingsLib", 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 90 | 91 | 93 | 94 | 95 | 101 | 102 | 104 | 105 | 107 | 108 | 109 | 110 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: DisplayFeatures 2 | 3 | * Table of contents 4 | :PROPERTIES: 5 | :TOC: 6 | :END: 7 | :CONTENTS: 8 | - [[#about][About]] 9 | - [[#usage][Usage]] 10 | - [[#purpose][Purpose]] 11 | - [[#credits][Credits]] 12 | :END: 13 | 14 | * About 15 | 16 | ROM Frontend for display features such as HBM and DC Dimming. 17 | 18 | * Usage 19 | 20 | ** 1) Clone this repository to the specified directory 21 | 22 | #+BEGIN_SRC shell 23 | git clone https://github.com/cyberknight777/android_packages_apps_DisplayFeatures packages/apps/DisplayFeatures 24 | #+END_SRC 25 | 26 | ** 2) Adapt the following commit for your device and build it as it is :) 27 | 28 | https://github.com/YAAP/device_xiaomi_sunny/commit/07e0b8cd0946fd619effc094b6d7026f5bbc8e2e 29 | 30 | * Purpose 31 | 32 | + To provide simple toggles for display features. 33 | 34 | #+END_SRC 35 | * Credits 36 | 37 | + [[https://t.me/cyberknight777][cyberknight777]] for developing this package. 38 | + [[https://t.me/CannedShroud][CannedShroud]] for helping with this package. 39 | + [[https://t.me/SakthivelNadar][Sakthivel Nadar]] for helping with this package. 40 | + [[https://t.me/Waxaranai][Waxaranai]] for helping with this package. 41 | + [[https://t.me/Idoybh2][Ido Ben-Hur]] for helping with this package. 42 | + [[https://t.me/CoolDude6942][CoolDude]] for helping with this package. 43 | + To whoever I imported/adapted from. 44 | -------------------------------------------------------------------------------- /res/drawable/ic_dc_dimming_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /res/drawable/ic_dc_dimming_on.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /res/drawable/ic_fps.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /res/drawable/ic_hbm.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/layout/displayfeatures.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /res/layout/preference_widget_switch_compat.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 26 | -------------------------------------------------------------------------------- /res/values-es-rES/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | Funciones de la Pantalla 19 | Contiene interruptores para el Modo de Brillo Alto y Modo Anti-Parpadeo 20 | 21 | 22 | Modo Anti-Parpadeo 23 | Reduce la fatiga visual en condiciones de poca luz (Nota: reduce la calidad de la imagen y la producción de color) 24 | El modo anti-parpadeo no esta actualmente soportado por el kernel 25 | 26 | 27 | Modo De Brillo Alto 28 | Permite que el brillo del panel de visualización supere el valor máximo de stock. Lo que ayuda a mejorar la visibilidad con luz solar directa. 29 | El modo de brillo alto no esta actualmente soportado por el kernel 30 | 31 | 32 | Superposición de FPS 33 | Muestra una superposición con los fotogramas por segundo actuales 34 | El kernel no admite actualmente la superposición de FPS 35 | 36 | 37 | -------------------------------------------------------------------------------- /res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | Các tính năng hiển thị 19 | Tuỳ chỉnh độ sáng cao và chống rung hình (DC Dimming) 20 | 21 | 22 | Chống rung hình 23 | Giảm mỏi mắt trong điều kiện ánh sáng thấp (Ghi chú: giảm chất lượng hình ảnh và màu sắc hiển thị) 24 | Kernel này hiện không hỗ trợ DC dimming 25 | 26 | 27 | Độ sáng cao 28 | Cho phép độ sáng màn hình vượt mức giá trị tối đa. Giúp tăng cường hiển thị trong môi trường ánh sáng mặt trời chiếu trực tiếp 29 | Kernel này hiện không hỗ trợ độ sáng cao 30 | 31 | 32 | -------------------------------------------------------------------------------- /res/values/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | Display Features 19 | Contains toggles for HBM and DC Dimming 20 | 21 | 22 | Anti-Flicker Mode 23 | Reduces eye strain in low light conditions (Note: reduces image quality and color production) 24 | DC Dimming is currently not supported by the kernel 25 | 26 | 27 | High Brightness Mode 28 | Allows the display panel brightness to go above the maximum stock value. Which helps in improving visibility in direct sunlight 29 | HBM is currently not supported by the kernel 30 | 31 | 32 | FPS Overlay 33 | Shows overlay with current frames per second 34 | FPS Overlay is currently not supported by the kernel 35 | 36 | 37 | -------------------------------------------------------------------------------- /res/values/symbols.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /res/xml/displayfeatures_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 26 | 27 | 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/BootCompletedReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The CyanogenMod Project 3 | * 2017-2019 The LineageOS Project 4 | * 2023 cyberknight777 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package com.android.displayfeatures; 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.util.Log; 25 | import com.android.displayfeatures.display.DisplayFeaturesConfig; 26 | import com.android.displayfeatures.utils.FileUtils; 27 | import android.content.SharedPreferences; 28 | import androidx.preference.PreferenceManager; 29 | 30 | public class BootCompletedReceiver extends BroadcastReceiver { 31 | private static final boolean DEBUG = false; 32 | private static final String TAG = "DisplayFeatures"; 33 | 34 | @Override 35 | public void onReceive(final Context context, Intent intent) { 36 | if (DEBUG) 37 | Log.d(TAG, "Received boot completed intent"); 38 | 39 | DisplayFeaturesConfig mConfig = DisplayFeaturesConfig.getInstance(context); 40 | 41 | SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 42 | 43 | boolean dcDimmingEnabled = sharedPrefs.getBoolean(mConfig.DISPLAYFEATURES_DC_DIMMING_KEY, false); 44 | boolean hbmEnabled = sharedPrefs.getBoolean(mConfig.DISPLAYFEATURES_HBM_KEY, false); 45 | FileUtils.writeLine(mConfig.getDcDimPath(), dcDimmingEnabled ? "1" : "0"); 46 | FileUtils.writeLine(mConfig.getHbmPath(), hbmEnabled ? "1" : "0"); 47 | 48 | // reset prefs that reflect a state that does not retain a reboot 49 | sharedPrefs.edit().remove(mConfig.PREF_KEY_FPS_STATE).commit(); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/SettingsSearchIndexablesProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Yet Another AOSP Project 3 | * 2023 cyberknight777 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.displayfeatures; 19 | 20 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME; 21 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID; 22 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION; 23 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS; 24 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE; 25 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK; 26 | import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID; 27 | import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; 28 | import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS; 29 | import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS; 30 | 31 | import android.database.Cursor; 32 | import android.database.MatrixCursor; 33 | import android.provider.SearchIndexableResource; 34 | import android.provider.SearchIndexablesProvider; 35 | 36 | import java.util.ArrayList; 37 | import java.util.Arrays; 38 | import java.util.Collection; 39 | import java.util.List; 40 | import java.util.Map; 41 | 42 | import com.android.displayfeatures.display.DisplayFeaturesActivity; 43 | 44 | public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider { 45 | 46 | private static final List RESOURCES = new ArrayList<>( 47 | Arrays.asList( 48 | new SearchIndexableResource(1, R.xml.displayfeatures_settings, 49 | DisplayFeaturesActivity.class.getName(), 0))); 50 | 51 | @Override 52 | public boolean onCreate() { 53 | return true; 54 | } 55 | 56 | @Override 57 | public Cursor queryXmlResources(String[] projection) { 58 | final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS); 59 | for (SearchIndexableResource resource : RESOURCES) { 60 | final Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length]; 61 | ref[COLUMN_INDEX_XML_RES_RANK] = resource.rank; 62 | ref[COLUMN_INDEX_XML_RES_RESID] = resource.xmlResId; 63 | ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = null; 64 | ref[COLUMN_INDEX_XML_RES_ICON_RESID] = resource.iconResId; 65 | ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = "com.android.settings.action.IA_SETTINGS"; 66 | ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = "com.android.displayfeatures"; 67 | ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = resource.className; 68 | cursor.addRow(ref); 69 | } 70 | return cursor; 71 | } 72 | 73 | @Override 74 | public Cursor queryRawData(String[] projection) { 75 | return new MatrixCursor(INDEXABLES_RAW_COLUMNS); 76 | } 77 | 78 | @Override 79 | public Cursor queryNonIndexableKeys(String[] projection) { 80 | return new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2016 The CyanogenMod Project 3 | * 2020 YAAP 4 | * 2023 cyberknight777 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package com.android.displayfeatures.display; 20 | 21 | import android.os.Bundle; 22 | import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; 23 | 24 | public class DisplayFeaturesActivity extends CollapsingToolbarBaseActivity { 25 | 26 | private static final String TAG_DISPLAYFEATURES = "displayfeatures"; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | getSupportFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, 32 | new DisplayFeaturesFragment(), TAG_DISPLAYFEATURES).commit(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 cyberknight777 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.displayfeatures.display; 18 | 19 | import android.content.Context; 20 | import android.content.res.Resources; 21 | 22 | import com.android.displayfeatures.utils.FileUtils; 23 | 24 | public class DisplayFeaturesConfig { 25 | 26 | private static DisplayFeaturesConfig instance = null; 27 | 28 | public static DisplayFeaturesConfig getInstance(Context context) { 29 | 30 | if (instance == null) { 31 | instance = new DisplayFeaturesConfig(context.getApplicationContext()); 32 | } 33 | 34 | return instance; 35 | } 36 | 37 | public static final String DISPLAYFEATURES_DC_DIMMING_KEY = "dc_dimming"; 38 | public static final String DISPLAYFEATURES_HBM_KEY = "hbm"; 39 | public static final String DISPLAYFEATURES_FPS_KEY = "fps"; 40 | 41 | private final String config_DisplayFeaturesDcDimPath; 42 | private final String config_DisplayFeaturesHbmPath; 43 | private final String config_DisplayFeaturesFpsPath; 44 | 45 | public static final String ACTION_HBM_SERVICE_CHANGED = "com.android.displayfeatures.display.HBM_SERVICE_CHANGED"; 46 | public static final String EXTRA_HBM_STATE = "hbmenabled"; 47 | 48 | public static final String ACTION_DC_DIM_SERVICE_CHANGED = "com.android.displayfeatures.display.DC_DIM_SERVICE_CHANGED"; 49 | public static final String EXTRA_DC_DIM_STATE = "dcdimenabled"; 50 | 51 | public static final String ACTION_FPS_SERVICE_CHANGED = "com.android.displayfeatures.display.FPS_SERVICE_CHANGED"; 52 | public static final String EXTRA_FPS_STATE = "fpsenabled"; 53 | public static final String PREF_KEY_FPS_STATE = "fps_running"; 54 | 55 | private DisplayFeaturesConfig(Context context) { 56 | 57 | Resources res = context.getResources(); 58 | 59 | config_DisplayFeaturesDcDimPath = res.getString(com.android.displayfeatures.R.string.config_DisplayFeaturesDcDimPath); 60 | config_DisplayFeaturesHbmPath = res.getString(com.android.displayfeatures.R.string.config_DisplayFeaturesHbmPath); 61 | config_DisplayFeaturesFpsPath = res.getString(com.android.displayfeatures.R.string.config_DisplayFeaturesFpsPath); 62 | 63 | } 64 | 65 | public String getDcDimPath() { 66 | return config_DisplayFeaturesDcDimPath; 67 | } 68 | 69 | public String getHbmPath() { 70 | return config_DisplayFeaturesHbmPath; 71 | } 72 | 73 | public String getFpsPath() { 74 | return config_DisplayFeaturesFpsPath; 75 | } 76 | 77 | public boolean isCurrentlyEnabled(String node) { 78 | return FileUtils.getNodeValueAsBoolean(node, false); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesDcDimTileService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 cyberknight777 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.displayfeatures.display; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.IntentFilter; 23 | import android.content.SharedPreferences; 24 | import android.graphics.drawable.Icon; 25 | import android.os.Handler; 26 | import android.os.UserHandle; 27 | import android.service.quicksettings.Tile; 28 | import android.service.quicksettings.TileService; 29 | 30 | import androidx.preference.PreferenceManager; 31 | 32 | import com.android.displayfeatures.R; 33 | import com.android.displayfeatures.utils.FileUtils; 34 | 35 | public class DisplayFeaturesDcDimTileService extends TileService { 36 | 37 | private DisplayFeaturesConfig mConfig; 38 | 39 | private Intent mDcDimIntent; 40 | 41 | private boolean mInternalStart; 42 | 43 | private final BroadcastReceiver mServiceStateReceiver = new BroadcastReceiver() { 44 | @Override 45 | public void onReceive(Context context, Intent intent) { 46 | if (mInternalStart) { 47 | mInternalStart = false; 48 | return; 49 | } 50 | updateUI(); 51 | } 52 | }; 53 | 54 | private void updateUI() { 55 | final Tile tile = getQsTile(); 56 | boolean enabled = mConfig.isCurrentlyEnabled(mConfig.getDcDimPath()); 57 | 58 | if (!enabled) tryStopService(); 59 | 60 | tile.setIcon(Icon.createWithResource(this, enabled ? R.drawable.ic_dc_dimming_on : R.drawable.ic_dc_dimming_off)); 61 | tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); 62 | tile.updateTile(); 63 | } 64 | 65 | @Override 66 | public void onStartListening() { 67 | super.onStartListening(); 68 | mConfig = DisplayFeaturesConfig.getInstance(this); 69 | 70 | updateUI(); 71 | 72 | IntentFilter filter = new IntentFilter(mConfig.ACTION_DC_DIM_SERVICE_CHANGED); 73 | registerReceiver(mServiceStateReceiver, filter); 74 | } 75 | 76 | @Override 77 | public void onStopListening() { 78 | super.onStopListening(); 79 | unregisterReceiver(mServiceStateReceiver); 80 | } 81 | 82 | @Override 83 | public void onClick() { 84 | super.onClick(); 85 | mInternalStart = true; 86 | 87 | SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 88 | 89 | boolean enabled = !mConfig.isCurrentlyEnabled(mConfig.getDcDimPath()); 90 | FileUtils.writeLine(mConfig.getDcDimPath(), enabled ? "1" : "0"); 91 | 92 | sharedPrefs.edit().putBoolean(mConfig.DISPLAYFEATURES_DC_DIMMING_KEY, enabled).commit(); 93 | 94 | Intent intent = new Intent(mConfig.ACTION_DC_DIM_SERVICE_CHANGED); 95 | 96 | intent.putExtra(mConfig.EXTRA_DC_DIM_STATE, enabled); 97 | intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 98 | this.sendBroadcastAsUser(intent, UserHandle.CURRENT);; 99 | 100 | updateUI(); 101 | } 102 | 103 | private void tryStopService() { 104 | if (mDcDimIntent == null) return; 105 | this.stopService(mDcDimIntent); 106 | mDcDimIntent = null; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesFpsService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The OmniROM Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.displayfeatures.display; 18 | 19 | import android.app.Service; 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.IntentFilter; 24 | import android.content.SharedPreferences; 25 | import android.graphics.Canvas; 26 | import android.graphics.Color; 27 | import android.graphics.Paint; 28 | import android.graphics.PixelFormat; 29 | import android.graphics.Typeface; 30 | import android.os.Handler; 31 | import android.os.IBinder; 32 | import android.os.Looper; 33 | import android.os.Message; 34 | import android.os.RemoteException; 35 | import android.os.ServiceManager; 36 | import android.os.UserHandle; 37 | import android.service.dreams.DreamService; 38 | import android.service.dreams.IDreamManager; 39 | import android.view.Gravity; 40 | import android.view.View; 41 | import android.view.WindowManager; 42 | import android.util.Log; 43 | 44 | import androidx.preference.PreferenceManager; 45 | 46 | import java.io.BufferedReader; 47 | import java.io.FileReader; 48 | import java.lang.Math; 49 | 50 | public class DisplayFeaturesFpsService extends Service { 51 | 52 | private View mView; 53 | private Thread mCurFPSThread; 54 | private final String TAG = "DisplayFeaturesFpsService"; 55 | private String mFps = null; 56 | 57 | private DisplayFeaturesConfig mConfig; 58 | private final String mFpsPath; 59 | 60 | private IDreamManager mDreamManager; 61 | 62 | public DisplayFeaturesFpsService() { 63 | super(); 64 | mConfig = DisplayFeaturesConfig.getInstance(this); 65 | mFpsPath = mConfig.getFpsPath(); 66 | } 67 | 68 | private class FPSView extends View { 69 | private final Paint mOnlinePaint; 70 | private final float mAscent; 71 | private final int mMaxWidth; 72 | 73 | private int mNeededWidth; 74 | private int mNeededHeight; 75 | private boolean mDataAvail; 76 | 77 | private final Handler mCurFPSHandler = new Handler(Looper.getMainLooper()) { 78 | public void handleMessage(Message msg) { 79 | if (msg.obj == null) return; 80 | if (msg.what == 1) { 81 | String msgData = (String) msg.obj; 82 | msgData = msgData.substring(0, Math.min(msgData.length(), 9)); 83 | mFps = msgData; 84 | mDataAvail = true; 85 | updateDisplay(); 86 | } 87 | } 88 | }; 89 | 90 | FPSView(Context c) { 91 | super(c); 92 | final float density = c.getResources().getDisplayMetrics().density; 93 | final int paddingPx = Math.round(5 * density); 94 | setPadding(paddingPx, paddingPx, paddingPx, paddingPx); 95 | setBackgroundColor(Color.argb(0x60, 0, 0, 0)); 96 | 97 | final int textSize = Math.round(12 * density); 98 | 99 | Typeface typeface = Typeface.create("monospace", Typeface.NORMAL); 100 | 101 | mOnlinePaint = new Paint(); 102 | mOnlinePaint.setTypeface(typeface); 103 | mOnlinePaint.setAntiAlias(true); 104 | mOnlinePaint.setTextSize(textSize); 105 | mOnlinePaint.setColor(Color.WHITE); 106 | mOnlinePaint.setShadowLayer(5.0f, 0.0f, 0.0f, Color.BLACK); 107 | 108 | mAscent = mOnlinePaint.ascent(); 109 | 110 | final String maxWidthStr="fps: 60.1"; 111 | mMaxWidth = (int) mOnlinePaint.measureText(maxWidthStr); 112 | 113 | updateDisplay(); 114 | } 115 | 116 | @Override 117 | protected void onAttachedToWindow() { 118 | super.onAttachedToWindow(); 119 | } 120 | 121 | @Override 122 | protected void onDetachedFromWindow() { 123 | super.onDetachedFromWindow(); 124 | mCurFPSHandler.removeMessages(1); 125 | } 126 | 127 | @Override 128 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 129 | setMeasuredDimension(resolveSize(mNeededWidth, widthMeasureSpec), 130 | resolveSize(mNeededHeight, heightMeasureSpec)); 131 | } 132 | 133 | private String getFpsString() { 134 | return mFps; 135 | } 136 | 137 | @Override 138 | public void onDraw(Canvas canvas) { 139 | super.onDraw(canvas); 140 | if (!mDataAvail) { 141 | return; 142 | } 143 | final int LEFT = getWidth() - 1; 144 | final int y = mPaddingTop - (int)mAscent; 145 | canvas.drawText(getFpsString(), 146 | LEFT-mPaddingLeft-mMaxWidth, 147 | y - 1, mOnlinePaint); 148 | } 149 | 150 | void updateDisplay() { 151 | if (!mDataAvail) { 152 | return; 153 | } 154 | 155 | int neededWidth = mPaddingLeft + mPaddingRight + mMaxWidth; 156 | int neededHeight = mPaddingTop + mPaddingBottom + 40; 157 | if (neededWidth != mNeededWidth || neededHeight != mNeededHeight) { 158 | mNeededWidth = neededWidth; 159 | mNeededHeight = neededHeight; 160 | requestLayout(); 161 | } else { 162 | invalidate(); 163 | } 164 | } 165 | 166 | public Handler getHandler(){ 167 | return mCurFPSHandler; 168 | } 169 | } 170 | 171 | protected static class CurFPSThread extends Thread { 172 | private boolean mInterrupt = false; 173 | private final Handler mHandler; 174 | private final String mFpsPath; 175 | 176 | public CurFPSThread(Handler handler, String path){ 177 | mHandler=handler; 178 | this.mFpsPath = path; 179 | } 180 | 181 | public void interrupt() { 182 | mInterrupt = true; 183 | } 184 | 185 | @Override 186 | public void run() { 187 | try { 188 | while (!mInterrupt) { 189 | sleep(1000); 190 | String fpsVal = DisplayFeaturesFpsService.readOneLine(this.mFpsPath); 191 | mHandler.sendMessage(mHandler.obtainMessage(1, fpsVal)); 192 | } 193 | } catch (InterruptedException ignored) { } 194 | } 195 | } 196 | 197 | @Override 198 | public void onCreate() { 199 | super.onCreate(); 200 | 201 | mView = new FPSView(this); 202 | WindowManager.LayoutParams params = new WindowManager.LayoutParams( 203 | WindowManager.LayoutParams.WRAP_CONTENT, 204 | WindowManager.LayoutParams.WRAP_CONTENT, 205 | WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, 206 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| 207 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 208 | PixelFormat.TRANSLUCENT); 209 | params.gravity = Gravity.END | Gravity.TOP; 210 | params.y = 50; 211 | params.setTitle("FPS Info"); 212 | 213 | startThread(); 214 | 215 | mDreamManager = IDreamManager.Stub.asInterface( 216 | ServiceManager.checkService(DreamService.DREAM_SERVICE)); 217 | IntentFilter screenStateFilter = new IntentFilter(Intent.ACTION_SCREEN_ON); 218 | screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF); 219 | registerReceiver(mScreenStateReceiver, screenStateFilter); 220 | 221 | WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); 222 | wm.addView(mView, params); 223 | 224 | // declare service is running 225 | final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 226 | sharedPrefs.edit().putBoolean(mConfig.PREF_KEY_FPS_STATE, true).commit(); 227 | } 228 | 229 | @Override 230 | public void onDestroy() { 231 | super.onDestroy(); 232 | stopThread(); 233 | ((WindowManager)getSystemService(WINDOW_SERVICE)).removeView(mView); 234 | mView = null; 235 | unregisterReceiver(mScreenStateReceiver); 236 | 237 | // declare service isn't running 238 | final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 239 | sharedPrefs.edit().remove(mConfig.PREF_KEY_FPS_STATE).commit(); 240 | } 241 | 242 | @Override 243 | public IBinder onBind(Intent intent) { 244 | return null; 245 | } 246 | 247 | private static String readOneLine(String path) { 248 | BufferedReader br; 249 | String line; 250 | try { 251 | br = new BufferedReader(new FileReader(path), 512); 252 | try { 253 | line = br.readLine(); 254 | } finally { 255 | br.close(); 256 | } 257 | } catch (Exception e) { 258 | return null; 259 | } 260 | return line; 261 | } 262 | 263 | private final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { 264 | @Override 265 | public void onReceive(Context context, Intent intent) { 266 | if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { 267 | Log.d(TAG, "ACTION_SCREEN_ON " + isDozeMode()); 268 | if (!isDozeMode()) { 269 | startThread(); 270 | mView.setVisibility(View.VISIBLE); 271 | } 272 | } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 273 | Log.d(TAG, "ACTION_SCREEN_OFF"); 274 | mView.setVisibility(View.GONE); 275 | stopThread(); 276 | } 277 | } 278 | }; 279 | 280 | private boolean isDozeMode() { 281 | try { 282 | if (mDreamManager != null && mDreamManager.isDreaming()) { 283 | return true; 284 | } 285 | } catch (RemoteException e) { 286 | return false; 287 | } 288 | return false; 289 | } 290 | 291 | private void startThread() { 292 | Log.d(TAG, "started CurFPSThread"); 293 | mCurFPSThread = new CurFPSThread(mView.getHandler(), mFpsPath); 294 | mCurFPSThread.start(); 295 | broadcastServiceState(true); 296 | } 297 | 298 | private void stopThread() { 299 | if (mCurFPSThread != null && mCurFPSThread.isAlive()) { 300 | Log.d(TAG, "stopping CurFPSThread"); 301 | mCurFPSThread.interrupt(); 302 | try { 303 | mCurFPSThread.join(); 304 | } catch (InterruptedException ignored) { } 305 | } 306 | mCurFPSThread = null; 307 | broadcastServiceState(false); 308 | } 309 | 310 | private void broadcastServiceState(boolean started) { 311 | Intent intent = new Intent(mConfig.ACTION_FPS_SERVICE_CHANGED); 312 | intent.putExtra(mConfig.EXTRA_FPS_STATE, started); 313 | intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 314 | sendBroadcastAsUser(intent, UserHandle.CURRENT); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesFpsTileService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 YAAP 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.displayfeatures.display; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.IntentFilter; 23 | import android.content.SharedPreferences; 24 | import android.service.quicksettings.Tile; 25 | import android.service.quicksettings.TileService; 26 | 27 | import androidx.preference.PreferenceManager; 28 | 29 | // TODO: Add FPS drawables 30 | public class DisplayFeaturesFpsTileService extends TileService { 31 | 32 | private boolean mIsShowing = false; 33 | private boolean mInternalStart = false; 34 | 35 | private DisplayFeaturesConfig mConfig; 36 | 37 | private final BroadcastReceiver mServiceStateReceiver = new BroadcastReceiver() { 38 | @Override 39 | public void onReceive(Context context, Intent intent) { 40 | if (mInternalStart) { 41 | mInternalStart = false; 42 | return; 43 | } 44 | mIsShowing = intent.getBooleanExtra(mConfig.EXTRA_FPS_STATE, false); 45 | updateTile(); 46 | } 47 | }; 48 | 49 | public DisplayFeaturesFpsTileService() { } 50 | 51 | @Override 52 | public void onStartListening() { 53 | super.onStartListening(); 54 | mIsShowing = isRunning(); 55 | updateTile(); 56 | IntentFilter filter = new IntentFilter(mConfig.ACTION_FPS_SERVICE_CHANGED); 57 | registerReceiver(mServiceStateReceiver, filter); 58 | } 59 | 60 | @Override 61 | public void onStopListening() { 62 | super.onStopListening(); 63 | unregisterReceiver(mServiceStateReceiver); 64 | } 65 | 66 | @Override 67 | public void onClick() { 68 | mInternalStart = true; 69 | Intent fpsinfo = new Intent(this, com.android.displayfeatures.display.DisplayFeaturesFpsService.class); 70 | mIsShowing = isRunning(); 71 | if (!mIsShowing) this.startService(fpsinfo); 72 | else this.stopService(fpsinfo); 73 | mIsShowing = !mIsShowing; 74 | updateTile(); 75 | } 76 | 77 | private void updateTile() { 78 | final Tile tile = getQsTile(); 79 | tile.setState(mIsShowing ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); 80 | tile.updateTile(); 81 | } 82 | 83 | private boolean isRunning() { 84 | final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 85 | return sharedPrefs.getBoolean(mConfig.PREF_KEY_FPS_STATE, false); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 YAAP 3 | * Copyright (C) 2023 cyberknight777 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.displayfeatures.display; 19 | 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.IntentFilter; 24 | import android.content.SharedPreferences; 25 | import android.os.Bundle; 26 | import android.os.Handler; 27 | import android.os.UserHandle; 28 | import androidx.preference.Preference; 29 | import androidx.preference.PreferenceFragmentCompat; 30 | import androidx.preference.PreferenceManager; 31 | import androidx.preference.SwitchPreferenceCompat; 32 | 33 | import com.android.displayfeatures.R; 34 | import com.android.displayfeatures.utils.FileUtils; 35 | 36 | public class DisplayFeaturesFragment extends PreferenceFragmentCompat implements 37 | Preference.OnPreferenceChangeListener { 38 | 39 | private SwitchPreferenceCompat mDcDimmingPreference; 40 | private SwitchPreferenceCompat mHBMPreference; 41 | private SwitchPreferenceCompat mFpsPreference; 42 | private DisplayFeaturesConfig mConfig; 43 | private boolean mInternalHbmStart = false; 44 | private boolean mInternalDcDimStart = false; 45 | private boolean mInternalFpsStart = false; 46 | 47 | private final BroadcastReceiver mServiceStateReceiver = new BroadcastReceiver() { 48 | @Override 49 | public void onReceive(Context context, Intent intent) { 50 | String action = intent.getAction(); 51 | if (action.equals(mConfig.ACTION_HBM_SERVICE_CHANGED)) { 52 | if (mInternalHbmStart) { 53 | mInternalHbmStart = false; 54 | return; 55 | } 56 | 57 | if (mHBMPreference == null) return; 58 | 59 | final boolean hbmStarted = intent.getBooleanExtra( 60 | mConfig.EXTRA_HBM_STATE, false); 61 | 62 | mHBMPreference.setChecked(hbmStarted); 63 | 64 | } else if (action.equals(mConfig.ACTION_DC_DIM_SERVICE_CHANGED)) { 65 | if (mInternalDcDimStart) { 66 | mInternalDcDimStart = false; 67 | return; 68 | } 69 | 70 | if (mDcDimmingPreference == null) return; 71 | 72 | final boolean dcDimStarted = intent.getBooleanExtra( 73 | mConfig.EXTRA_DC_DIM_STATE, false); 74 | 75 | mDcDimmingPreference.setChecked(dcDimStarted); 76 | 77 | } else if (action.equals(mConfig.ACTION_FPS_SERVICE_CHANGED)) { 78 | if (mInternalFpsStart) { 79 | mInternalFpsStart = false; 80 | return; 81 | } 82 | 83 | if (mFpsPreference == null) return; 84 | 85 | final boolean fpsStarted = intent.getBooleanExtra( 86 | mConfig.EXTRA_FPS_STATE, false); 87 | 88 | mFpsPreference.setChecked(fpsStarted); 89 | } 90 | } 91 | }; 92 | 93 | @Override 94 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 95 | setPreferencesFromResource(R.xml.displayfeatures_settings, rootKey); 96 | mConfig = DisplayFeaturesConfig.getInstance(getContext()); 97 | mDcDimmingPreference = (SwitchPreferenceCompat) findPreference(mConfig.DISPLAYFEATURES_DC_DIMMING_KEY); 98 | if (FileUtils.fileExists(mConfig.getDcDimPath())) { 99 | mDcDimmingPreference.setEnabled(true); 100 | mDcDimmingPreference.setOnPreferenceChangeListener(this); 101 | } else { 102 | mDcDimmingPreference.setSummary(R.string.dc_dimming_summary_not_supported); 103 | mDcDimmingPreference.setEnabled(false); 104 | } 105 | mHBMPreference = (SwitchPreferenceCompat) findPreference(mConfig.DISPLAYFEATURES_HBM_KEY); 106 | if (FileUtils.fileExists(mConfig.getHbmPath())) { 107 | mHBMPreference.setEnabled(true); 108 | mHBMPreference.setOnPreferenceChangeListener(this); 109 | } else { 110 | mHBMPreference.setSummary(R.string.hbm_summary_not_supported); 111 | mHBMPreference.setEnabled(false); 112 | } 113 | mFpsPreference = (SwitchPreferenceCompat) findPreference(mConfig.DISPLAYFEATURES_FPS_KEY); 114 | if (FileUtils.fileExists(mConfig.getFpsPath())) mFpsPreference.setOnPreferenceChangeListener(this); 115 | else mFpsPreference.setSummary(R.string.fps_summary_not_supported); 116 | 117 | mDcDimmingPreference.setChecked(mConfig.isCurrentlyEnabled(mConfig.getDcDimPath())); 118 | mHBMPreference.setChecked(mConfig.isCurrentlyEnabled(mConfig.getHbmPath())); 119 | mFpsPreference.setChecked(isFpsOverlayRunning()); 120 | 121 | // Registering observers 122 | IntentFilter filter = new IntentFilter(); 123 | filter.addAction(mConfig.ACTION_HBM_SERVICE_CHANGED); 124 | filter.addAction(mConfig.ACTION_DC_DIM_SERVICE_CHANGED); 125 | filter.addAction(mConfig.ACTION_FPS_SERVICE_CHANGED); 126 | getContext().registerReceiver(mServiceStateReceiver, filter); 127 | } 128 | 129 | @Override 130 | public void onResume() { 131 | super.onResume(); 132 | mDcDimmingPreference.setChecked(mConfig.isCurrentlyEnabled(mConfig.getDcDimPath())); 133 | mHBMPreference.setChecked(mConfig.isCurrentlyEnabled(mConfig.getHbmPath())); 134 | mFpsPreference.setChecked(isFpsOverlayRunning()); 135 | } 136 | 137 | 138 | @Override 139 | public boolean onPreferenceChange(Preference preference, Object newValue) { 140 | if (mConfig.DISPLAYFEATURES_DC_DIMMING_KEY.equals(preference.getKey())) { 141 | mInternalHbmStart = true; 142 | Context mContext = getContext(); 143 | 144 | SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); 145 | 146 | FileUtils.writeLine(mConfig.getDcDimPath(), (Boolean) newValue ? "1":"0"); 147 | 148 | boolean enabled = mConfig.isCurrentlyEnabled(mConfig.getDcDimPath()); 149 | 150 | sharedPrefs.edit().putBoolean(mConfig.DISPLAYFEATURES_DC_DIMMING_KEY, enabled).commit(); 151 | 152 | Intent intent = new Intent(mConfig.ACTION_DC_DIM_SERVICE_CHANGED); 153 | 154 | intent.putExtra(mConfig.EXTRA_DC_DIM_STATE, enabled); 155 | intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 156 | mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);; 157 | } 158 | if (mConfig.DISPLAYFEATURES_HBM_KEY.equals(preference.getKey())) { 159 | mInternalHbmStart = true; 160 | Context mContext = getContext(); 161 | 162 | FileUtils.writeLine(mConfig.getHbmPath(), (Boolean) newValue ? "1" : "0"); 163 | 164 | boolean enabled = mConfig.isCurrentlyEnabled(mConfig.getHbmPath()); 165 | 166 | Intent hbmIntent = new Intent(mContext, 167 | com.android.displayfeatures.display.DisplayFeaturesHbmService.class); 168 | 169 | if (enabled) mContext.startService(hbmIntent); 170 | else mContext.stopService(hbmIntent); 171 | 172 | Intent intent = new Intent(mConfig.ACTION_HBM_SERVICE_CHANGED); 173 | 174 | intent.putExtra(mConfig.EXTRA_HBM_STATE, enabled); 175 | intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 176 | mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);; 177 | } 178 | if (mConfig.DISPLAYFEATURES_FPS_KEY.equals(preference.getKey())) { 179 | mInternalFpsStart = true; 180 | Context mContext = getContext(); 181 | 182 | boolean enabled = (Boolean) newValue; 183 | Intent fpsinfo = new Intent(mContext, 184 | com.android.displayfeatures.display.DisplayFeaturesFpsService.class); 185 | if (enabled) mContext.startService(fpsinfo); 186 | else mContext.stopService(fpsinfo); 187 | } 188 | return true; 189 | } 190 | 191 | 192 | @Override 193 | public void onDestroy() { 194 | super.onDestroy(); 195 | getContext().unregisterReceiver(mServiceStateReceiver); 196 | } 197 | 198 | private boolean isFpsOverlayRunning() { 199 | Context context = getContext(); 200 | final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 201 | return sharedPrefs.getBoolean(mConfig.PREF_KEY_FPS_STATE, false); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesHbmService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Yet Another AOSP Project 3 | * 2023 cyberknight777 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | package com.android.displayfeatures.display; 20 | 21 | import android.app.Service; 22 | import android.content.BroadcastReceiver; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.IntentFilter; 26 | import android.os.IBinder; 27 | import android.os.Handler; 28 | import android.os.UserHandle; 29 | 30 | import com.android.displayfeatures.utils.FileUtils; 31 | 32 | public class DisplayFeaturesHbmService extends Service { 33 | 34 | private DisplayFeaturesConfig mConfig; 35 | 36 | private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 37 | @Override 38 | public void onReceive(Context context, Intent intent) { 39 | String action = intent.getAction(); 40 | mConfig = DisplayFeaturesConfig.getInstance(context); 41 | 42 | if (action.equals(Intent.ACTION_SCREEN_OFF)) { 43 | boolean enabled = mConfig.isCurrentlyEnabled(mConfig.getHbmPath()); 44 | if (enabled) FileUtils.writeLine(mConfig.getHbmPath(), "0"); 45 | Intent hbmIntent = new Intent(context, 46 | com.android.displayfeatures.display.DisplayFeaturesHbmService.class); 47 | 48 | if (enabled) context.startService(hbmIntent); 49 | else context.stopService(hbmIntent); 50 | 51 | Intent mChangedIntent = new Intent(mConfig.ACTION_HBM_SERVICE_CHANGED); 52 | 53 | mChangedIntent.putExtra(mConfig.EXTRA_HBM_STATE, enabled); 54 | mChangedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 55 | context.sendBroadcastAsUser(mChangedIntent, UserHandle.CURRENT);; 56 | stopSelf(); 57 | } 58 | } 59 | }; 60 | 61 | @Override 62 | public int onStartCommand(Intent intent, int flags, int startId) { 63 | IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 64 | registerReceiver(mReceiver, intentFilter); 65 | return START_REDELIVER_INTENT; 66 | } 67 | 68 | @Override 69 | public IBinder onBind(Intent intent) { return null; } 70 | 71 | @Override 72 | public void onDestroy() { 73 | unregisterReceiver(mReceiver); 74 | super.onDestroy(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/display/DisplayFeaturesHbmTileService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 cyberknight777 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.displayfeatures.display; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.IntentFilter; 23 | import android.content.SharedPreferences; 24 | import android.os.Handler; 25 | import android.os.UserHandle; 26 | import android.service.quicksettings.Tile; 27 | import android.service.quicksettings.TileService; 28 | 29 | import androidx.preference.PreferenceManager; 30 | 31 | import com.android.displayfeatures.R; 32 | import com.android.displayfeatures.utils.FileUtils; 33 | 34 | public class DisplayFeaturesHbmTileService extends TileService { 35 | 36 | private DisplayFeaturesConfig mConfig; 37 | 38 | private Intent mHbmIntent; 39 | 40 | private boolean mInternalStart; 41 | 42 | private final BroadcastReceiver mServiceStateReceiver = new BroadcastReceiver() { 43 | @Override 44 | public void onReceive(Context context, Intent intent) { 45 | if (mInternalStart) { 46 | mInternalStart = false; 47 | return; 48 | } 49 | updateUI(); 50 | } 51 | }; 52 | 53 | private void updateUI() { 54 | final Tile tile = getQsTile(); 55 | boolean enabled = mConfig.isCurrentlyEnabled(mConfig.getHbmPath()); 56 | 57 | if (!enabled) tryStopService(); 58 | 59 | tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); 60 | tile.updateTile(); 61 | } 62 | 63 | @Override 64 | public void onStartListening() { 65 | super.onStartListening(); 66 | mConfig = DisplayFeaturesConfig.getInstance(this); 67 | 68 | updateUI(); 69 | 70 | IntentFilter filter = new IntentFilter(mConfig.ACTION_HBM_SERVICE_CHANGED); 71 | registerReceiver(mServiceStateReceiver, filter); 72 | } 73 | 74 | @Override 75 | public void onStopListening() { 76 | super.onStopListening(); 77 | unregisterReceiver(mServiceStateReceiver); 78 | } 79 | 80 | @Override 81 | public void onClick() { 82 | super.onClick(); 83 | mInternalStart = true; 84 | 85 | boolean enabled = !mConfig.isCurrentlyEnabled(mConfig.getHbmPath()); 86 | FileUtils.writeLine(mConfig.getHbmPath(), enabled ? "1" : "0"); 87 | 88 | Intent hbmIntent = new Intent(this, 89 | com.android.displayfeatures.display.DisplayFeaturesHbmService.class); 90 | 91 | if (enabled) this.startService(hbmIntent); 92 | else this.stopService(hbmIntent); 93 | 94 | Intent intent = new Intent(mConfig.ACTION_HBM_SERVICE_CHANGED); 95 | 96 | intent.putExtra(mConfig.EXTRA_HBM_STATE, enabled); 97 | intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 98 | this.sendBroadcastAsUser(intent, UserHandle.CURRENT);; 99 | 100 | updateUI(); 101 | } 102 | 103 | private void tryStopService() { 104 | if (mHbmIntent == null) return; 105 | this.stopService(mHbmIntent); 106 | mHbmIntent = null; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/com/android/displayfeatures/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The CyanogenMod Project 3 | * Copyright (C) 2023 cyberknight777 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.displayfeatures.utils; 19 | 20 | import android.util.Log; 21 | import java.io.BufferedReader; 22 | import java.io.BufferedWriter; 23 | import java.io.File; 24 | import java.io.FileNotFoundException; 25 | import java.io.FileReader; 26 | import java.io.FileWriter; 27 | import java.io.IOException; 28 | 29 | public final class FileUtils { 30 | private static final String TAG = "FileUtils"; 31 | 32 | private FileUtils() { 33 | // This class is not supposed to be instantiated 34 | } 35 | 36 | /** 37 | * Reads the first line of text from the given file. 38 | * Reference {@link BufferedReader#readLine()} for clarification on what a 39 | * line is 40 | * 41 | * @return the read line contents, or null on failure 42 | */ 43 | public static String readOneLine(String fileName) { 44 | String line = null; 45 | 46 | try (BufferedReader reader = 47 | new BufferedReader(new FileReader(fileName), 512)) { 48 | line = reader.readLine(); 49 | } catch (FileNotFoundException e) { 50 | Log.w(TAG, "No such file " + fileName + " for reading", e); 51 | } catch (IOException e) { 52 | Log.e(TAG, "Could not read from file " + fileName, e); 53 | } 54 | 55 | return line; 56 | } 57 | 58 | /** 59 | * Writes the given value into the given file 60 | * 61 | * @return true on success, false on failure 62 | */ 63 | public static boolean writeLine(String fileName, String value) { 64 | 65 | try (BufferedWriter writer = 66 | new BufferedWriter(new FileWriter(fileName))) { 67 | writer.write(value); 68 | } catch (FileNotFoundException e) { 69 | Log.w(TAG, "No such file " + fileName + " for writing", e); 70 | return false; 71 | } catch (IOException e) { 72 | Log.e(TAG, "Could not write to file " + fileName, e); 73 | return false; 74 | } 75 | 76 | return true; 77 | } 78 | 79 | /** 80 | * Checks whether the given file exists 81 | * 82 | * @return true if exists, false if not 83 | */ 84 | public static boolean fileExists(String fileName) { 85 | final File file = new File(fileName); 86 | return file.exists(); 87 | } 88 | 89 | /** 90 | * Checks whether the given file is readable 91 | * 92 | * @return true if readable, false if not 93 | */ 94 | public static boolean isFileReadable(String fileName) { 95 | final File file = new File(fileName); 96 | return file.exists() && file.canRead(); 97 | } 98 | 99 | /** 100 | * Checks whether the given file is writable 101 | * 102 | * @return true if writable, false if not 103 | */ 104 | public static boolean isFileWritable(String fileName) { 105 | final File file = new File(fileName); 106 | return file.exists() && file.canWrite(); 107 | } 108 | 109 | /** 110 | * Deletes an existing file 111 | * 112 | * @return true if the delete was successful, false if not 113 | */ 114 | public static boolean delete(String fileName) { 115 | final File file = new File(fileName); 116 | boolean ok = false; 117 | try { 118 | ok = file.delete(); 119 | } catch (SecurityException e) { 120 | Log.w(TAG, "SecurityException trying to delete " + fileName, e); 121 | } 122 | return ok; 123 | } 124 | 125 | /** 126 | * Renames an existing file 127 | * 128 | * @return true if the rename was successful, false if not 129 | */ 130 | public static boolean rename(String srcPath, String dstPath) { 131 | final File srcFile = new File(srcPath); 132 | final File dstFile = new File(dstPath); 133 | boolean ok = false; 134 | try { 135 | ok = srcFile.renameTo(dstFile); 136 | } catch (SecurityException e) { 137 | Log.w(TAG, 138 | "SecurityException trying to rename " + srcPath + " to " + dstPath, 139 | e); 140 | } catch (NullPointerException e) { 141 | Log.e(TAG, 142 | "NullPointerException trying to rename " + srcPath + " to " + 143 | dstPath, 144 | e); 145 | } 146 | return ok; 147 | } 148 | 149 | /** 150 | * Gets value of a node as a boolean 151 | * 152 | * @return default value that is passed by function caller 153 | */ 154 | public static boolean getNodeValueAsBoolean(String filename, boolean defValue) { 155 | String fileValue = readOneLine(filename); 156 | if (fileValue != null) { 157 | return (!fileValue.equals("0")); 158 | } 159 | return defValue; 160 | } 161 | } 162 | --------------------------------------------------------------------------------