├── 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 |
--------------------------------------------------------------------------------