a52sxq: Introduce parts [1/2]
Based on XiaomiParts for peridot Special thanks to kenway214
This commit is contained in:
@@ -18,6 +18,10 @@ DEVICE_PATH := device/samsung/a52sxq
|
||||
|
||||
DEVICE_PACKAGE_OVERLAYS += $(DEVICE_PATH)/overlay
|
||||
|
||||
# Core Packages
|
||||
PRODUCT_PACKAGES += \
|
||||
Parts
|
||||
|
||||
# call the common setup
|
||||
$(call inherit-product, device/samsung/sm7325-common/common.mk)
|
||||
|
||||
|
||||
43
parts/Android.bp
Normal file
43
parts/Android.bp
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (C) 2017-2020 The LineageOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
android_app {
|
||||
name: "Parts",
|
||||
defaults: [
|
||||
"SettingsLibDefaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
"src/**/*.java"
|
||||
],
|
||||
|
||||
certificate: "platform",
|
||||
resource_dirs: ["res"],
|
||||
platform_apis: true,
|
||||
system_ext_specific: true,
|
||||
privileged: true,
|
||||
|
||||
static_libs: [
|
||||
"androidx.core_core",
|
||||
"org.lineageos.settings.resources"
|
||||
],
|
||||
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
},
|
||||
|
||||
required: [
|
||||
"privapp_whitelist_org.lineageos.settings.xml",
|
||||
],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "privapp_whitelist_org.lineageos.settings.xml",
|
||||
src: "permissions/privapp_whitelist_org.lineageos.settings.xml",
|
||||
sub_dir: "permissions",
|
||||
system_ext_specific: true,
|
||||
}
|
||||
164
parts/AndroidManifest.xml
Normal file
164
parts/AndroidManifest.xml
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015-2016 The CyanogenMod Project
|
||||
2017-2018 The LineageOS Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.lineageos.settings"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="24"
|
||||
android:targetSdkVersion="30"/>
|
||||
|
||||
<application
|
||||
android:label="@string/app_title"
|
||||
android:persistent="true"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
android:directBootAware="true"
|
||||
android:theme="@style/Theme.SubSettingsBase">
|
||||
|
||||
<receiver
|
||||
android:name=".BootCompletedReceiver"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:replace="android:authorities"/>
|
||||
|
||||
<receiver
|
||||
android:name=".Startup"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Per-app refresh rate activity -->
|
||||
<activity
|
||||
android:name=".refreshrate.RefreshActivity"
|
||||
android:label="@string/refresh_title"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.action.IA_SETTINGS" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="com.android.settings.category"
|
||||
android:value="com.android.settings.category.ia.display" />
|
||||
<meta-data
|
||||
android:name="com.android.settings.summary"
|
||||
android:resource="@string/refresh_summary" />
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".refreshrate.RefreshService"
|
||||
android:exported="true"
|
||||
android:permission="RefreshService">
|
||||
</service>
|
||||
|
||||
<!-- Refresh Rate tile service -->
|
||||
<service
|
||||
android:name=".refreshrate.RefreshTileService"
|
||||
android:icon="@drawable/ic_qs_refresh_rate"
|
||||
android:label="@string/refresh_title_tile_label"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.service.quicksettings.TOGGLEABLE_TILE"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<!-- TileHandler activity -->
|
||||
<activity
|
||||
android:name=".TileHandlerActivity"
|
||||
android:exported="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- GameBar Overlay -->
|
||||
<activity
|
||||
android:name=".gameoverlay.GameOverlaySettingsActivity"
|
||||
android:label="@string/game_overlay_title"
|
||||
android:theme="@style/Theme.SubSettingsBase"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.action.IA_SETTINGS" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="com.android.settings.category"
|
||||
android:value="com.android.settings.category.ia.apps" />
|
||||
<meta-data
|
||||
android:name="com.android.settings.summary"
|
||||
android:resource="@string/game_overlay_summary" />
|
||||
</activity>
|
||||
|
||||
<!-- GameBar Overlay Tile Service -->
|
||||
<service
|
||||
android:name=".gameoverlay.GameOverlayTileService"
|
||||
android:label="@string/game_overlay_tile_label"
|
||||
android:icon="@drawable/ic_gamebar_overlay_tile"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.service.quicksettings.TOGGLEABLE_TILE"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<!-- GameBar BootReceiver -->
|
||||
<receiver
|
||||
android:name="org.lineageos.settings.gameoverlay.GameOverlayBootReceiver"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 Paranoid Android
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<permissions>
|
||||
<privapp-permissions package="org.lineageos.settings">
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
3
parts/proguard.flags
Normal file
3
parts/proguard.flags
Normal file
@@ -0,0 +1,3 @@
|
||||
-keep class org.lineageos.settings.refreshrate.* {
|
||||
*;
|
||||
}
|
||||
10
parts/res/drawable/ic_gamebar_overlay_tile.xml
Normal file
10
parts/res/drawable/ic_gamebar_overlay_tile.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M189,800Q129,800 86.5,757Q44,714 42,653Q42,644 43,635Q44,626 46,617L130,281Q144,227 187,193.5Q230,160 285,160L675,160Q730,160 773,193.5Q816,227 830,281L914,617Q916,626 917.5,635.5Q919,645 919,654Q919,715 875.5,757.5Q832,800 771,800Q729,800 693,778Q657,756 639,718L611,660Q606,650 596,645Q586,640 575,640L385,640Q374,640 364,645Q354,650 349,660L321,718Q303,756 267,778Q231,800 189,800ZM540.12,433.33Q554.33,433.33 563.83,423.72Q573.33,414.1 573.33,399.88Q573.33,385.67 563.72,376.17Q554.1,366.67 539.88,366.67Q525.67,366.67 516.17,376.28Q506.67,385.9 506.67,400.12Q506.67,414.33 516.28,423.83Q525.9,433.33 540.12,433.33ZM620.12,353.33Q634.33,353.33 643.83,343.72Q653.33,334.1 653.33,319.88Q653.33,305.67 643.72,296.17Q634.1,286.67 619.88,286.67Q605.67,286.67 596.17,296.28Q586.67,305.9 586.67,320.12Q586.67,334.33 596.28,343.83Q605.9,353.33 620.12,353.33ZM620.12,513.33Q634.33,513.33 643.83,503.72Q653.33,494.1 653.33,479.88Q653.33,465.67 643.72,456.17Q634.1,446.67 619.88,446.67Q605.67,446.67 596.17,456.28Q586.67,465.9 586.67,480.12Q586.67,494.33 596.28,503.83Q605.9,513.33 620.12,513.33ZM700.12,433.33Q714.33,433.33 723.83,423.72Q733.33,414.1 733.33,399.88Q733.33,385.67 723.72,376.17Q714.1,366.67 699.88,366.67Q685.67,366.67 676.17,376.28Q666.67,385.9 666.67,400.12Q666.67,414.33 676.28,423.83Q685.9,433.33 700.12,433.33ZM340.08,496.67Q351.67,496.67 359.17,489.11Q366.67,481.56 366.67,470L366.67,426.67L410,426.67Q421.56,426.67 429.11,419.09Q436.67,411.51 436.67,399.92Q436.67,388.33 429.11,380.83Q421.56,373.33 410,373.33L366.67,373.33L366.67,330Q366.67,318.44 359.09,310.89Q351.51,303.33 339.92,303.33Q328.33,303.33 320.83,310.89Q313.33,318.44 313.33,330L313.33,373.33L270,373.33Q258.44,373.33 250.89,380.91Q243.33,388.49 243.33,400.08Q243.33,411.67 250.89,419.17Q258.44,426.67 270,426.67L313.33,426.67L313.33,470Q313.33,481.56 320.91,489.11Q328.49,496.67 340.08,496.67Z"/>
|
||||
</vector>
|
||||
29
parts/res/drawable/ic_qs_refresh_rate.xml
Normal file
29
parts/res/drawable/ic_qs_refresh_rate.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M11.9929646 8.32347523L12.1291792 8.32347523L12.1291792
|
||||
7.1871225L14.4387395 9.0621225L12.1291792 10.9371225L12.1291792
|
||||
9.82347523L11.9929646 9.82347523C10.7503239 9.82347523 9.74296457
|
||||
10.8308345 9.74296457 12.0734752C9.74296457 13.3161159 10.7503239
|
||||
14.3234752 11.9929646 14.3234752C13.2356053 14.3234752 14.2429646
|
||||
13.3161159 14.2429646 12.0734752L15.7429646 12.0734752C15.7429646
|
||||
14.144543 14.0640324 15.8234752 11.9929646 15.8234752C9.92189676
|
||||
15.8234752 8.24296457 14.144543 8.24296457 12.0734752C8.24296457
|
||||
10.0024074 9.92189676 8.32347523 11.9929646 8.32347523ZM7 2.75C6.86192881
|
||||
2.75 6.75 2.86192881 6.75 3L6.75 21.0105977C6.75 21.1486689 6.86192881
|
||||
21.2605977 7 21.2605977L16.9859291 21.2605977C17.1240003 21.2605977
|
||||
17.2359291 21.1486689 17.2359291 21.0105977L17.2359291 3C17.2359291
|
||||
2.86192881 17.1240003 2.75 16.9859291 2.75L7 2.75ZM7 1.25L16.9859291
|
||||
1.25C17.9524275 1.25 18.7359291 2.03350169 18.7359291 3L18.7359291
|
||||
21.0105977C18.7359291 21.977096 17.9524275 22.7605977 16.9859291
|
||||
22.7605977L7 22.7605977C6.03350169 22.7605977 5.25 21.977096 5.25
|
||||
21.0105977L5.25 3C5.25 2.03350169 6.03350169 1.25 7 1.25Z"
|
||||
tools:ignore="VectorPath" />
|
||||
</vector>
|
||||
22
parts/res/drawable/ic_refresh_120.xml
Normal file
22
parts/res/drawable/ic_refresh_120.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorAccent">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M17 1.01L7 1C5.9 1 5 1.9 5 3v4h2V3h10v18H7v-4H5v4c0 1.1 0.9 2 2 2h10c1.1 0 2-0.9 2-2V3c0-1.1-0.9-1.99-2-1.99Z"/>
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M1 8v1.56h1.56v6.22H4.1V8H1Z"/>
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M4.89 8v1.56H8v1.55H6.44c-0.4 0-0.8 0.17-1.1 0.46-0.29 0.29-0.45 0.68-0.45 1.1v3.1h4.67v-1.55H6.44v-1.55H8c0.41 0 0.8-0.17 1.1-0.46 0.3-0.3 0.46-0.69 0.46-1.1V9.56c0-0.42-0.17-0.81-0.46-1.1C8.8 8.16 8.41 8 8 8H4.89Z"/>
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:strokeColor="#ff000000"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M11.89 9.06h-0.5v0.5 4.66 0.5h0.5 1.55 0.5v-0.5-4.66-0.5h-0.5-1.55ZM11.14 8.8c0.2-0.2 0.47-0.31 0.75-0.31h1.55c0.28 0 0.55 0.11 0.75 0.3 0.2 0.2 0.31 0.48 0.31 0.76v4.66c0 0.28-0.11 0.55-0.3 0.75-0.2 0.2-0.48 0.3-0.76 0.3H11.9c-0.28 0-0.55-0.1-0.75-0.3-0.2-0.2-0.3-0.47-0.3-0.75V9.56c0-0.28 0.1-0.55 0.3-0.75Z"/>
|
||||
</vector>
|
||||
17
parts/res/drawable/ic_refresh_60.xml
Normal file
17
parts/res/drawable/ic_refresh_60.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorAccent">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M6.55 8c-0.4 0-0.8 0.16-1.1 0.46C5.17 8.75 5 9.14 5 9.56v4.66c0 0.4 0.16 0.8 0.46 1.1 0.29 0.29 0.68 0.45 1.1 0.45H8.1c0.41 0 0.8-0.16 1.1-0.46 0.29-0.29 0.45-0.68 0.45-1.1v-1.55c0-0.41-0.16-0.8-0.45-1.1-0.3-0.29-0.69-0.45-1.1-0.45H6.55V9.55h3.11V8h-3.1Zm0 4.66h1.56v1.56H6.55v-1.56Z"/>
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M12 8c-0.42 0-0.81 0.16-1.1 0.46-0.3 0.29-0.46 0.68-0.46 1.1v4.66c0 0.4 0.16 0.8 0.45 1.1 0.3 0.29 0.69 0.45 1.1 0.45h1.56c0.4 0 0.8-0.16 1.1-0.46 0.29-0.29 0.45-0.68 0.45-1.1V9.56c0-0.4-0.16-0.8-0.46-1.1C14.35 8.17 13.96 8 13.54 8H12Zm0 1.55h1.55v4.67h-1.56V9.55Z"/>
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M17 1.01L7 1C5.9 1 5 1.9 5 3v4h2V3h10v18H7v-4H5v4c0 1.1 0.9 2 2 2h10c1.1 0 2-0.9 2-2V3c0-1.1-0.9-1.99-2-1.99Z"/>
|
||||
</vector>
|
||||
11
parts/res/drawable/ic_refresh_default.xml
Normal file
11
parts/res/drawable/ic_refresh_default.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorAccent">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M17 3.01V15l2 2V3.01c0-1.1-0.9-1.99-2-1.99L7 1C6 0.99 5 1.99 5 2.99L7 5V3.01h10Zm4.2 18.19L19 19.01l-2-2-10-10-2-2-2.19-2.19L1.4 4.23 5 7.84v13.17c0 1.1 0.9 2 2 2h10c0.85 0 1.58-0.55 1.87-1.3l0.91 0.91 1.41-1.42ZM17 21.01H7L17 21v-0.99 1Zm-10 0V9.84l10 10.17V21L7 21.01Z"/>
|
||||
</vector>
|
||||
9
parts/res/drawable/ic_scenes.xml
Normal file
9
parts/res/drawable/ic_scenes.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- drawable/music_clef_bass.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path android:fillColor="#000" android:pathData="M18.5 5A1.5 1.5 0 1 1 17 6.5A1.5 1.5 0 0 1 18.5 5M18.5 11A1.5 1.5 0 1 1 17 12.5A1.5 1.5 0 0 1 18.5 11M10 4A5 5 0 0 0 5 9V10A2 2 0 1 0 7.18 8A3 3 0 0 1 10 6A4 4 0 0 1 14 10C14 13.59 11.77 16.19 7 18.2L7.76 20.04C13.31 17.72 16 14.43 16 10A6 6 0 0 0 10 4Z" />
|
||||
</vector>
|
||||
12
parts/res/layout/activity_game_overlay.xml
Normal file
12
parts/res/layout/activity_game_overlay.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/game_overlay_fragment"
|
||||
android:name="org.lineageos.settings.gameoverlay.GameOverlayFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
10
parts/res/layout/game_overlay.xml
Normal file
10
parts/res/layout/game_overlay.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/game_overlay_root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#80000000"
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
18
parts/res/layout/refresh_layout.xml
Normal file
18
parts/res/layout/refresh_layout.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The LineageOS Project
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/refresh_rv_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
64
parts/res/layout/refresh_list_item.xml
Normal file
64
parts/res/layout/refresh_list_item.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The LineageOS Project
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingTop="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@null"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Medium"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/app_mode"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_refresh_default" />
|
||||
</LinearLayout>
|
||||
98
parts/res/values/arrays.xml
Normal file
98
parts/res/values/arrays.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2025 The Android Open Source Project
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources>
|
||||
<!-- Update Interval -->
|
||||
<string-array name="fps_overlay_update_interval_entries">
|
||||
<item>Every 500ms</item>
|
||||
<item>Every second</item>
|
||||
<item>Every 2 seconds</item>
|
||||
<item>Every 5 seconds</item>
|
||||
</string-array>
|
||||
<string-array name="fps_overlay_update_interval_values">
|
||||
<item>500</item>
|
||||
<item>1000</item>
|
||||
<item>2000</item>
|
||||
<item>5000</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Position -->
|
||||
<string-array name="fps_overlay_position_entries">
|
||||
<item>Top Left</item>
|
||||
<item>Top Center</item>
|
||||
<item>Top Right</item>
|
||||
<item>Bottom Left</item>
|
||||
<item>Bottom Center</item>
|
||||
<item>Bottom Right</item>
|
||||
<item>Custom Draggable</item>
|
||||
</string-array>
|
||||
<string-array name="fps_overlay_position_values">
|
||||
<item>top_left</item>
|
||||
<item>top_center</item>
|
||||
<item>top_right</item>
|
||||
<item>bottom_left</item>
|
||||
<item>bottom_center</item>
|
||||
<item>bottom_right</item>
|
||||
<item>draggable</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Overlay color -->
|
||||
<string-array name="fps_overlay_color_entries">
|
||||
<item>White</item>
|
||||
<item>Crimson</item>
|
||||
<item>Fruit Salad</item>
|
||||
<item>Royal Blue</item>
|
||||
<item>Amber</item>
|
||||
<item>Teal</item>
|
||||
<item>Electric Violet</item>
|
||||
<item>Magenta</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="fps_overlay_color_values">
|
||||
<item>#FFFFFF</item>
|
||||
<item>#DC143C</item>
|
||||
<item>#4CAF50</item>
|
||||
<item>#4169E1</item>
|
||||
<item>#FFBF00</item>
|
||||
<item>#008080</item>
|
||||
<item>#8A2BE2</item>
|
||||
<item>#FF1493</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Overlay format -->
|
||||
<string-array name="game_overlay_format_entries">
|
||||
<item>Full</item>
|
||||
<item>Minimal</item>
|
||||
</string-array>
|
||||
<string-array name="game_overlay_format_values">
|
||||
<item>full</item>
|
||||
<item>minimal</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Split Mode -->
|
||||
<string-array name="game_overlay_split_mode_entries">
|
||||
<item>Side-by-Side</item>
|
||||
<item>Stacked</item>
|
||||
</string-array>
|
||||
<string-array name="game_overlay_split_mode_values">
|
||||
<item>side_by_side</item>
|
||||
<item>stacked</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Long press timeouts -->
|
||||
<string-array name="game_overlay_longpress_entries">
|
||||
<item>1 second</item>
|
||||
<item>3 seconds</item>
|
||||
<item>5 seconds</item>
|
||||
<item>10 seconds</item>
|
||||
</string-array>
|
||||
<string-array name="game_overlay_longpress_values">
|
||||
<item>1000</item>
|
||||
<item>3000</item>
|
||||
<item>5000</item>
|
||||
<item>10000</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
54
parts/res/values/strings.xml
Normal file
54
parts/res/values/strings.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The LineageOS Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<!-- Core Settings -->
|
||||
<string name="app_title">Core Settings</string>
|
||||
<string name="tile_add">Add tile</string>
|
||||
<string name="tile_added">Tile added</string>
|
||||
<string name="tile_not_added">Tile not added</string>
|
||||
<string name="tile_already_added">Tile already added</string>
|
||||
<string name="tile_on">On</string>
|
||||
<string name="tile_off">Off</string>
|
||||
|
||||
<!-- Per-app refresh rate -->
|
||||
<string name="refresh_title">Per-app refresh rate</string>
|
||||
<string name="refresh_title_tile_label">Refresh rate</string>
|
||||
<string name="refresh_summary">Set the maximum refresh rate for a specific application</string>
|
||||
<string name="refresh_default">Default</string>
|
||||
<string name="refresh_standard">60Hz</string>
|
||||
<string name="refresh_extreme">120Hz</string>
|
||||
|
||||
<!-- GameBar Overlay -->
|
||||
<string name="game_overlay_title">GameBar</string>
|
||||
<string name="game_overlay_summary">Enable the system overlay (FPS, Temp, etc.)</string>
|
||||
<string name="overlay_permission_required">Overlay permission is required</string>
|
||||
<string name="overlay_permission_granted">Overlay permission granted</string>
|
||||
<string name="overlay_permission_denied">Overlay permission denied</string>
|
||||
<string name="game_overlay_tile_label">GameBar</string>
|
||||
<string name="game_overlay_tile_description">Toggle the game overlay</string>
|
||||
|
||||
<!-- Quick Settings Customization -->
|
||||
<string name="qs_panel_settings_title">QS tiles Customization</string>
|
||||
<string name="qs_panel_settings_summary">Customize how tiles are displayed in quick settings</string>
|
||||
<string name="qs_tile_style">QS tiles style</string>
|
||||
<string name="qs_num_titles">Portrait tile grid</string>
|
||||
<string name="qs_num_rows">Rows</string>
|
||||
<string name="qs_num_columns">Columns</string>
|
||||
<string name="qs_num_rows_landscape">Rows</string>
|
||||
<string name="qs_num_columns_landscape">Columns</string>
|
||||
</resources>
|
||||
221
parts/res/xml/game_overlay_preferences.xml
Normal file
221
parts/res/xml/game_overlay_preferences.xml
Normal file
@@ -0,0 +1,221 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_enable"
|
||||
android:title="Enable GameBar Overlay"
|
||||
android:summary="@string/game_overlay_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Overlay Features"
|
||||
android:dependency="game_overlay_enable">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_fps_enable"
|
||||
android:title="FPS Overlay"
|
||||
android:summary="Show current FPS on screen"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_temp_enable"
|
||||
android:title="Device Temperature"
|
||||
android:summary="Show device (battery) temperature"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_cpu_usage_enable"
|
||||
android:title="CPU Usage"
|
||||
android:summary="Show current CPU usage percentage"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_cpu_clock_enable"
|
||||
android:title="CPU Clock Speeds"
|
||||
android:summary="Show current CPU clock speeds for each core"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_cpu_temp_enable"
|
||||
android:title="CPU Temperature"
|
||||
android:summary="Show CPU temperature (thermal_zone0)"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_ram_enable"
|
||||
android:title="RAM Usage"
|
||||
android:summary="Show current RAM usage in MB"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_gpu_usage_enable"
|
||||
android:title="GPU Usage"
|
||||
android:summary="Show GPU usage percentage"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_gpu_clock_enable"
|
||||
android:title="GPU Clock Speed"
|
||||
android:summary="Show current GPU clock frequency"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_gpu_temp_enable"
|
||||
android:title="GPU Temperature"
|
||||
android:summary="Show current GPU temperature"
|
||||
android:defaultValue="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Customization"
|
||||
android:dependency="game_overlay_enable">
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_overlay_text_size"
|
||||
android:title="Text Size"
|
||||
android:summary="Adjust the size of overlay text"
|
||||
android:defaultValue="16"
|
||||
android:max="32"
|
||||
android:min="12" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_overlay_background_alpha"
|
||||
android:title="Background Transparency"
|
||||
android:summary="Adjust the transparency of the background"
|
||||
android:defaultValue="128"
|
||||
android:max="255"
|
||||
android:min="0" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_overlay_corner_radius"
|
||||
android:title="Overlay Corner Radius"
|
||||
android:summary="Adjust how rounded the overlay corners should be"
|
||||
android:defaultValue="16"
|
||||
android:max="100"
|
||||
android:min="0" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_overlay_padding"
|
||||
android:title="Overlay Padding"
|
||||
android:summary="Adjust the space around the stats"
|
||||
android:defaultValue="12"
|
||||
android:max="64"
|
||||
android:min="0" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_overlay_item_spacing"
|
||||
android:title="Item Spacing"
|
||||
android:summary="Adjust spacing between overlay lines"
|
||||
android:defaultValue="8"
|
||||
android:max="50"
|
||||
android:min="0" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_update_interval"
|
||||
android:title="Update Interval"
|
||||
android:summary="Set how often the overlay values update"
|
||||
android:defaultValue="1000"
|
||||
android:entries="@array/fps_overlay_update_interval_entries"
|
||||
android:entryValues="@array/fps_overlay_update_interval_values" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_title_color"
|
||||
android:title="Stat Title Color"
|
||||
android:summary="Color for 'FPS', 'Temp', 'CPU', etc. text"
|
||||
android:defaultValue="#FFFFFF"
|
||||
android:entries="@array/fps_overlay_color_entries"
|
||||
android:entryValues="@array/fps_overlay_color_values" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_value_color"
|
||||
android:title="Stat Value Color"
|
||||
android:summary="Color for numeric stats (e.g., '29', '32.0°C')"
|
||||
android:defaultValue="#4CAF50"
|
||||
android:entries="@array/fps_overlay_color_entries"
|
||||
android:entryValues="@array/fps_overlay_color_values" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_position"
|
||||
android:title="Overlay Position"
|
||||
android:summary="Select the position of the overlay on screen"
|
||||
android:defaultValue="top_left"
|
||||
android:entries="@array/fps_overlay_position_entries"
|
||||
android:entryValues="@array/fps_overlay_position_values" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_format"
|
||||
android:title="Overlay Format"
|
||||
android:summary="Choose between Full or Minimal display"
|
||||
android:defaultValue="full"
|
||||
android:entries="@array/game_overlay_format_entries"
|
||||
android:entryValues="@array/game_overlay_format_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Split Config"
|
||||
android:dependency="game_overlay_enable">
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_split_mode"
|
||||
android:title="Split Mode"
|
||||
android:summary="Choose Side-by-Side or Stacked arrangement"
|
||||
android:defaultValue="stacked"
|
||||
android:entries="@array/game_overlay_split_mode_entries"
|
||||
android:entryValues="@array/game_overlay_split_mode_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Overlay Gesture Controls"
|
||||
android:dependency="game_overlay_enable">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_single_tap_toggle"
|
||||
android:title="Enable Single Tap to Toggle"
|
||||
android:summary="Tap once to switch between full and minimal overlay formats"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_doubletap_capture"
|
||||
android:title="Enable Double Tap to Capture"
|
||||
android:summary="Double-tap overlay to start/stop capture logs"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="game_overlay_longpress_enable"
|
||||
android:title="Enable Long Press"
|
||||
android:summary="Long-press overlay to access Gamebar settings"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_overlay_longpress_timeout"
|
||||
android:title="Long Press Duration"
|
||||
android:summary="Set the duration required to long-press the overlay"
|
||||
android:defaultValue="1000"
|
||||
android:entries="@array/game_overlay_longpress_entries"
|
||||
android:entryValues="@array/game_overlay_longpress_values"
|
||||
android:dependency="game_overlay_longpress_enable" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Capture Logs"
|
||||
android:dependency="game_overlay_enable">
|
||||
|
||||
<Preference
|
||||
android:key="game_overlay_capture_start"
|
||||
android:title="Start Logging"
|
||||
android:summary="Begin capturing FPS and performance data in real-time" />
|
||||
|
||||
<Preference
|
||||
android:key="game_overlay_capture_stop"
|
||||
android:title="Stop Logging"
|
||||
android:summary="Stop capturing FPS and performance data in real-time" />
|
||||
|
||||
<Preference
|
||||
android:key="game_overlay_capture_export"
|
||||
android:title="Export GameBar Log Data"
|
||||
android:summary="Save the captured FPS and performance data as a CSV file" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
75
parts/src/org/lineageos/settings/BootCompletedReceiver.java
Normal file
75
parts/src/org/lineageos/settings/BootCompletedReceiver.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The CyanogenMod Project
|
||||
* 2017-2019 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
||||
import org.lineageos.settings.refreshrate.RefreshUtils;
|
||||
|
||||
public class BootCompletedReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "Parts";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (DEBUG) Log.i(TAG, "Received intent: " + intent.getAction());
|
||||
switch (intent.getAction()) {
|
||||
case Intent.ACTION_LOCKED_BOOT_COMPLETED:
|
||||
handleLockedBootCompleted(context);
|
||||
break;
|
||||
case Intent.ACTION_BOOT_COMPLETED:
|
||||
handleBootCompleted(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLockedBootCompleted(Context context) {
|
||||
if (DEBUG) Log.i(TAG, "Handling locked boot completed.");
|
||||
try {
|
||||
// Start necessary services
|
||||
startServices(context);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during locked boot completed processing", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBootCompleted(Context context) {
|
||||
if (DEBUG) Log.i(TAG, "Handling boot completed.");
|
||||
// Add additional boot-completed actions if needed
|
||||
}
|
||||
|
||||
private void startServices(Context context) {
|
||||
if (DEBUG) Log.i(TAG, "Starting services...");
|
||||
|
||||
// Start Refresh Rate Service
|
||||
RefreshUtils.startService(context);
|
||||
}
|
||||
}
|
||||
93
parts/src/org/lineageos/settings/TileHandlerActivity.java
Normal file
93
parts/src/org/lineageos/settings/TileHandlerActivity.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.lineageos.settings.refreshrate.RefreshActivity;
|
||||
import org.lineageos.settings.refreshrate.RefreshTileService;
|
||||
import org.lineageos.settings.gameoverlay.GameOverlaySettingsActivity;
|
||||
import org.lineageos.settings.gameoverlay.GameOverlayTileService;
|
||||
|
||||
public final class TileHandlerActivity extends Activity {
|
||||
private static final String TAG = "TileHandlerActivity";
|
||||
|
||||
// Map QS Tile services to their corresponding activity
|
||||
private static final Map<String, Class<?>> TILE_ACTIVITY_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
TILE_ACTIVITY_MAP.put(RefreshTileService.class.getName(), RefreshActivity.class);
|
||||
TILE_ACTIVITY_MAP.put(GameOverlayTileService.class.getName(), GameOverlaySettingsActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
if (intent == null || !TileService.ACTION_QS_TILE_PREFERENCES.equals(intent.getAction())) {
|
||||
Log.e(TAG, "Invalid or null intent received");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final ComponentName qsTile = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME);
|
||||
if (qsTile == null) {
|
||||
Log.e(TAG, "No QS tile component found in intent");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final String qsName = qsTile.getClassName();
|
||||
final Intent targetIntent = new Intent();
|
||||
|
||||
// Check if the tile is mapped to an activity
|
||||
if (TILE_ACTIVITY_MAP.containsKey(qsName)) {
|
||||
targetIntent.setClass(this, TILE_ACTIVITY_MAP.get(qsName));
|
||||
Log.d(TAG, "Launching settings activity for QS tile: " + qsName);
|
||||
} else {
|
||||
// Default: Open app settings for the QS tile's package
|
||||
final String packageName = qsTile.getPackageName();
|
||||
if (packageName == null) {
|
||||
Log.e(TAG, "QS tile package name is null");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
targetIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
targetIntent.setData(Uri.fromParts("package", packageName, null));
|
||||
Log.d(TAG, "Opening app info for package: " + packageName);
|
||||
}
|
||||
|
||||
// Ensure proper navigation behavior
|
||||
targetIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK |
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
startActivity(targetIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
public class ForegroundAppDetector {
|
||||
|
||||
private static final String TAG = "ForegroundAppDetector";
|
||||
|
||||
public static String getForegroundPackageName(Context context) {
|
||||
|
||||
String pkg = tryGetRunningTasks(context);
|
||||
if (pkg != null) {
|
||||
return pkg;
|
||||
}
|
||||
pkg = tryReflectActivityTaskManager();
|
||||
if (pkg != null) {
|
||||
return pkg;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static String tryGetRunningTasks(Context context) {
|
||||
try {
|
||||
if (context.checkSelfPermission("android.permission.GET_TASKS")
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
|
||||
if (tasks != null && !tasks.isEmpty()) {
|
||||
ActivityManager.RunningTaskInfo top = tasks.get(0);
|
||||
if (top.topActivity != null) {
|
||||
return top.topActivity.getPackageName();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "GET_TASKS permission not granted to this system app?");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "tryGetRunningTasks error: ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String tryReflectActivityTaskManager() {
|
||||
try {
|
||||
Class<?> atmClass = Class.forName("android.app.ActivityTaskManager");
|
||||
Method getServiceMethod = atmClass.getDeclaredMethod("getService");
|
||||
getServiceMethod.setAccessible(true);
|
||||
Object atmService = getServiceMethod.invoke(null);
|
||||
Method getTasksMethod = atmService.getClass().getMethod("getTasks", int.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<?> taskList = (List<?>) getTasksMethod.invoke(atmService, 1);
|
||||
if (taskList != null && !taskList.isEmpty()) {
|
||||
|
||||
Object firstTask = taskList.get(0);
|
||||
|
||||
Class<?> rtiClass = firstTask.getClass();
|
||||
Method getTopActivityMethod = rtiClass.getDeclaredMethod("getTopActivity");
|
||||
Object compName = getTopActivityMethod.invoke(firstTask);
|
||||
if (compName != null) {
|
||||
|
||||
Method getPackageNameMethod = compName.getClass().getMethod("getPackageName");
|
||||
String pkgName = (String) getPackageNameMethod.invoke(compName);
|
||||
return pkgName;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "tryReflectActivityTaskManager error: ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
132
parts/src/org/lineageos/settings/gameoverlay/GameDataExport.java
Normal file
132
parts/src/org/lineageos/settings/gameoverlay/GameDataExport.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GameDataExport {
|
||||
|
||||
private static GameDataExport sInstance;
|
||||
public static synchronized GameDataExport getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new GameDataExport();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private boolean mCapturing = false;
|
||||
|
||||
private final List<String[]> mStatsRows = new ArrayList<>();
|
||||
|
||||
private static final String[] CSV_HEADER = {
|
||||
"DateTime",
|
||||
"PackageName",
|
||||
"FPS",
|
||||
"Battery_Temp",
|
||||
"CPU_Usage",
|
||||
"CPU_Temp",
|
||||
"GPU_Usage",
|
||||
"GPU_Clock",
|
||||
"GPU_Temp"
|
||||
};
|
||||
|
||||
private GameDataExport() {
|
||||
}
|
||||
|
||||
public void startCapture() {
|
||||
mCapturing = true;
|
||||
mStatsRows.clear();
|
||||
mStatsRows.add(CSV_HEADER);
|
||||
}
|
||||
|
||||
public void stopCapture() {
|
||||
mCapturing = false;
|
||||
}
|
||||
|
||||
public boolean isCapturing() {
|
||||
return mCapturing;
|
||||
}
|
||||
|
||||
public void addOverlayData(String dateTime,
|
||||
String packageName,
|
||||
String fps,
|
||||
String batteryTemp,
|
||||
String cpuUsage,
|
||||
String cpuTemp,
|
||||
String gpuUsage,
|
||||
String gpuClock,
|
||||
String gpuTemp) {
|
||||
if (!mCapturing) return;
|
||||
|
||||
String[] row = {
|
||||
dateTime,
|
||||
packageName,
|
||||
fps,
|
||||
batteryTemp,
|
||||
cpuUsage,
|
||||
cpuTemp,
|
||||
gpuUsage,
|
||||
gpuClock,
|
||||
gpuTemp
|
||||
};
|
||||
mStatsRows.add(row);
|
||||
}
|
||||
|
||||
public void exportDataToCsv() {
|
||||
if (mStatsRows.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||
File outFile = new File(Environment.getExternalStorageDirectory(), "GameBar_log_" + timeStamp + ".csv");
|
||||
|
||||
BufferedWriter bw = null;
|
||||
try {
|
||||
bw = new BufferedWriter(new FileWriter(outFile, true));
|
||||
for (String[] row : mStatsRows) {
|
||||
bw.write(toCsvLine(row));
|
||||
bw.newLine();
|
||||
}
|
||||
bw.flush();
|
||||
} catch (IOException ignored) {
|
||||
} finally {
|
||||
if (bw != null) {
|
||||
try { bw.close(); } catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String toCsvLine(String[] columns) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
sb.append(columns[i]);
|
||||
if (i < columns.length - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
776
parts/src/org/lineageos/settings/gameoverlay/GameOverlay.java
Normal file
776
parts/src/org/lineageos/settings/gameoverlay/GameOverlay.java
Normal file
@@ -0,0 +1,776 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GameOverlay {
|
||||
|
||||
private static GameOverlay sInstance;
|
||||
public static synchronized GameOverlay getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new GameOverlay(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private static final String FPS_PATH = "/sys/class/drm/sde-crtc-0/measured_fps";
|
||||
private static final String BATTERY_TEMP_PATH= "/sys/class/thermal/thermal_zone78/temp";
|
||||
|
||||
private static final String PREF_KEY_X = "game_overlay_x";
|
||||
private static final String PREF_KEY_Y = "game_overlay_y";
|
||||
|
||||
private final Context mContext;
|
||||
private final WindowManager mWindowManager;
|
||||
private final Handler mHandler;
|
||||
|
||||
private View mOverlayView;
|
||||
private LinearLayout mRootLayout;
|
||||
private WindowManager.LayoutParams mLayoutParams;
|
||||
private boolean mIsShowing = false;
|
||||
|
||||
private int mTextSizeSp = 16;
|
||||
private int mBackgroundAlpha = 128;
|
||||
private int mCornerRadius = 16;
|
||||
private int mPaddingDp = 12;
|
||||
private String mTitleColorHex = "#FFFFFF";
|
||||
private String mValueColorHex = "#FFFFFF";
|
||||
private String mPosition = "top_left";
|
||||
private String mSplitMode = "stacked";
|
||||
private String mOverlayFormat = "full";
|
||||
private int mUpdateIntervalMs = 1000;
|
||||
private boolean mDraggable = false;
|
||||
|
||||
private boolean mShowBatteryTemp= false;
|
||||
private boolean mShowCpuUsage = false;
|
||||
private boolean mShowCpuClock = false;
|
||||
private boolean mShowCpuTemp = false;
|
||||
private boolean mShowRam = false;
|
||||
private boolean mShowFps = false;
|
||||
|
||||
private boolean mShowGpuUsage = false;
|
||||
private boolean mShowGpuClock = false;
|
||||
private boolean mShowGpuTemp = false;
|
||||
|
||||
private boolean mLongPressEnabled = false;
|
||||
private long mLongPressThresholdMs = 1000;
|
||||
private boolean mPressActive = false;
|
||||
private float mDownX, mDownY;
|
||||
private static final float TOUCH_SLOP = 20f;
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
private boolean mDoubleTapCaptureEnabled = false;
|
||||
private boolean mSingleTapToggleEnabled = false;
|
||||
private GradientDrawable mBgDrawable;
|
||||
|
||||
private int mItemSpacingDp = 8;
|
||||
|
||||
private final Runnable mLongPressRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mPressActive) {
|
||||
openOverlaySettings();
|
||||
mPressActive = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsShowing) {
|
||||
updateStats();
|
||||
mHandler.postDelayed(this, mUpdateIntervalMs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private GameOverlay(Context context) {
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mBgDrawable = new GradientDrawable();
|
||||
applyBackgroundStyle();
|
||||
}
|
||||
|
||||
public void applyPreferences() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
|
||||
mShowFps = prefs.getBoolean("game_overlay_fps_enable", false);
|
||||
mShowBatteryTemp = prefs.getBoolean("game_overlay_temp_enable", false);
|
||||
mShowCpuUsage = prefs.getBoolean("game_overlay_cpu_usage_enable", false);
|
||||
mShowCpuClock = prefs.getBoolean("game_overlay_cpu_clock_enable", false);
|
||||
mShowCpuTemp = prefs.getBoolean("game_overlay_cpu_temp_enable", false);
|
||||
mShowRam = prefs.getBoolean("game_overlay_ram_enable", false);
|
||||
|
||||
mShowGpuUsage = prefs.getBoolean("game_overlay_gpu_usage_enable", false);
|
||||
mShowGpuClock = prefs.getBoolean("game_overlay_gpu_clock_enable", false);
|
||||
mShowGpuTemp = prefs.getBoolean("game_overlay_gpu_temp_enable", false);
|
||||
|
||||
mDoubleTapCaptureEnabled = prefs.getBoolean("game_overlay_doubletap_capture", false);
|
||||
mSingleTapToggleEnabled = prefs.getBoolean("game_overlay_single_tap_toggle", false);
|
||||
|
||||
updateSplitMode(prefs.getString("game_overlay_split_mode", "stacked"));
|
||||
updateTextSize(prefs.getInt("game_overlay_text_size", 16));
|
||||
updateBackgroundAlpha(prefs.getInt("game_overlay_background_alpha", 128));
|
||||
updateCornerRadius(prefs.getInt("game_overlay_corner_radius", 16));
|
||||
updatePadding(prefs.getInt("game_overlay_padding", 12));
|
||||
updateTitleColor(prefs.getString("game_overlay_title_color", "#FFFFFF"));
|
||||
updateValueColor(prefs.getString("game_overlay_value_color", "#4CAF50"));
|
||||
updateOverlayFormat(prefs.getString("game_overlay_format", "full"));
|
||||
updateUpdateInterval(prefs.getString("game_overlay_update_interval", "1000"));
|
||||
updatePosition(prefs.getString("game_overlay_position", "top_left"));
|
||||
|
||||
int spacing = prefs.getInt("game_overlay_item_spacing", 8);
|
||||
updateItemSpacing(spacing);
|
||||
|
||||
mLongPressEnabled = prefs.getBoolean("game_overlay_longpress_enable", false);
|
||||
String lpTimeoutStr = prefs.getString("game_overlay_longpress_timeout", "1000");
|
||||
try {
|
||||
long lpt = Long.parseLong(lpTimeoutStr);
|
||||
setLongPressThresholdMs(lpt);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (mIsShowing) return;
|
||||
|
||||
applyPreferences();
|
||||
|
||||
mLayoutParams = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT
|
||||
);
|
||||
|
||||
if ("draggable".equals(mPosition)) {
|
||||
mDraggable = true;
|
||||
loadSavedPosition(mLayoutParams);
|
||||
if (mLayoutParams.x == 0 && mLayoutParams.y == 0) {
|
||||
mLayoutParams.gravity = Gravity.TOP | Gravity.START;
|
||||
mLayoutParams.x = 0;
|
||||
mLayoutParams.y = 100;
|
||||
}
|
||||
} else {
|
||||
mDraggable = false;
|
||||
applyPosition(mLayoutParams, mPosition);
|
||||
}
|
||||
|
||||
mOverlayView = new LinearLayout(mContext);
|
||||
mOverlayView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
mRootLayout = (LinearLayout) mOverlayView;
|
||||
applySplitMode();
|
||||
applyBackgroundStyle();
|
||||
applyPadding();
|
||||
|
||||
mGestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (mDoubleTapCaptureEnabled) {
|
||||
if (GameDataExport.getInstance().isCapturing()) {
|
||||
GameDataExport.getInstance().stopCapture();
|
||||
Toast.makeText(mContext, "Capture Stopped", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
GameDataExport.getInstance().startCapture();
|
||||
Toast.makeText(mContext, "Capture Started", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onDoubleTap(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (mSingleTapToggleEnabled) {
|
||||
mOverlayFormat = "full".equals(mOverlayFormat) ? "minimal" : "full";
|
||||
Toast.makeText(mContext, "Overlay Format: " + mOverlayFormat, Toast.LENGTH_SHORT).show();
|
||||
updateStats();
|
||||
return true;
|
||||
}
|
||||
return super.onSingleTapConfirmed(e);
|
||||
}
|
||||
});
|
||||
|
||||
mOverlayView.setOnTouchListener((v, event) -> {
|
||||
if (mGestureDetector != null && mGestureDetector.onTouchEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mDraggable) {
|
||||
initialX = mLayoutParams.x;
|
||||
initialY = mLayoutParams.y;
|
||||
initialTouchX = event.getRawX();
|
||||
initialTouchY = event.getRawY();
|
||||
}
|
||||
if (mLongPressEnabled) {
|
||||
mPressActive = true;
|
||||
mDownX = event.getRawX();
|
||||
mDownY = event.getRawY();
|
||||
mHandler.postDelayed(mLongPressRunnable, mLongPressThresholdMs);
|
||||
}
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mLongPressEnabled && mPressActive) {
|
||||
float dx = Math.abs(event.getRawX() - mDownX);
|
||||
float dy = Math.abs(event.getRawY() - mDownY);
|
||||
if (dx > TOUCH_SLOP || dy > TOUCH_SLOP) {
|
||||
mPressActive = false;
|
||||
mHandler.removeCallbacks(mLongPressRunnable);
|
||||
}
|
||||
}
|
||||
if (mDraggable) {
|
||||
int deltaX = (int) (event.getRawX() - initialTouchX);
|
||||
int deltaY = (int) (event.getRawY() - initialTouchY);
|
||||
mLayoutParams.x = initialX + deltaX;
|
||||
mLayoutParams.y = initialY + deltaY;
|
||||
mWindowManager.updateViewLayout(mOverlayView, mLayoutParams);
|
||||
}
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (mLongPressEnabled && mPressActive) {
|
||||
mPressActive = false;
|
||||
mHandler.removeCallbacks(mLongPressRunnable);
|
||||
}
|
||||
if (mDraggable) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
prefs.edit()
|
||||
.putInt(PREF_KEY_X, mLayoutParams.x)
|
||||
.putInt(PREF_KEY_Y, mLayoutParams.y)
|
||||
.apply();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
mWindowManager.addView(mOverlayView, mLayoutParams);
|
||||
mIsShowing = true;
|
||||
startUpdates();
|
||||
}
|
||||
|
||||
private int initialX, initialY;
|
||||
private float initialTouchX, initialTouchY;
|
||||
|
||||
public void hide() {
|
||||
if (!mIsShowing) return;
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
if (mOverlayView != null) {
|
||||
mWindowManager.removeView(mOverlayView);
|
||||
mOverlayView = null;
|
||||
}
|
||||
mIsShowing = false;
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
if (!mIsShowing || mRootLayout == null) return;
|
||||
|
||||
mRootLayout.removeAllViews();
|
||||
|
||||
List<View> statViews = new ArrayList<>();
|
||||
|
||||
// 1) FPS
|
||||
float fpsVal = parseFps();
|
||||
String fpsStr = fpsVal >= 0 ? String.format(Locale.getDefault(), "%.1f", fpsVal) : "N/A";
|
||||
if (mShowFps) {
|
||||
statViews.add(createStatLine("FPS", fpsStr));
|
||||
}
|
||||
|
||||
// 2) Battery temp
|
||||
String batteryTempStr = "N/A";
|
||||
if (mShowBatteryTemp) {
|
||||
String tmp = readLine(BATTERY_TEMP_PATH);
|
||||
if (tmp != null && !tmp.isEmpty()) {
|
||||
try {
|
||||
int raw = Integer.parseInt(tmp.trim());
|
||||
float c = raw / 1000f;
|
||||
batteryTempStr = String.format(Locale.getDefault(), "%.1f", c);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
statViews.add(createStatLine("Temp", batteryTempStr + "°C"));
|
||||
}
|
||||
|
||||
// 3) CPU usage
|
||||
String cpuUsageStr = "N/A";
|
||||
if (mShowCpuUsage) {
|
||||
cpuUsageStr = GameOverlayCpuInfo.getCpuUsage();
|
||||
String display = "N/A".equals(cpuUsageStr) ? "N/A" : cpuUsageStr + "%";
|
||||
statViews.add(createStatLine("CPU", display));
|
||||
}
|
||||
|
||||
// 4) CPU freq
|
||||
if (mShowCpuClock) {
|
||||
List<String> freqs = GameOverlayCpuInfo.getCpuFrequencies();
|
||||
if (!freqs.isEmpty()) {
|
||||
statViews.add(buildCpuFreqView(freqs));
|
||||
}
|
||||
}
|
||||
|
||||
// 5) CPU temp
|
||||
String cpuTempStr = "N/A";
|
||||
if (mShowCpuTemp) {
|
||||
cpuTempStr = GameOverlayCpuInfo.getCpuTemp();
|
||||
statViews.add(createStatLine("CPU Temp", "N/A".equals(cpuTempStr) ? "N/A" : cpuTempStr + "°C"));
|
||||
}
|
||||
|
||||
// 6) RAM usage
|
||||
String ramStr = "N/A";
|
||||
if (mShowRam) {
|
||||
ramStr = GameOverlayMemInfo.getRamUsage();
|
||||
statViews.add(createStatLine("RAM", "N/A".equals(ramStr) ? "N/A" : ramStr + " MB"));
|
||||
}
|
||||
|
||||
// 7) GPU usage
|
||||
String gpuUsageStr = "N/A";
|
||||
if (mShowGpuUsage) {
|
||||
gpuUsageStr = GameOverlayGpuInfo.getGpuUsage();
|
||||
statViews.add(createStatLine("GPU", "N/A".equals(gpuUsageStr) ? "N/A" : gpuUsageStr + "%"));
|
||||
}
|
||||
|
||||
// 8) GPU clock
|
||||
String gpuClockStr = "N/A";
|
||||
if (mShowGpuClock) {
|
||||
gpuClockStr = GameOverlayGpuInfo.getGpuClock();
|
||||
statViews.add(createStatLine("GPU Freq", "N/A".equals(gpuClockStr) ? "N/A" : gpuClockStr + "MHz"));
|
||||
}
|
||||
|
||||
// 9) GPU temp
|
||||
String gpuTempStr = "N/A";
|
||||
if (mShowGpuTemp) {
|
||||
gpuTempStr = GameOverlayGpuInfo.getGpuTemp();
|
||||
statViews.add(createStatLine("GPU Temp", "N/A".equals(gpuTempStr) ? "N/A" : gpuTempStr + "°C"));
|
||||
}
|
||||
|
||||
if ("side_by_side".equals(mSplitMode)) {
|
||||
mRootLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
if ("minimal".equals(mOverlayFormat)) {
|
||||
for (int i = 0; i < statViews.size(); i++) {
|
||||
mRootLayout.addView(statViews.get(i));
|
||||
if (i < statViews.size() - 1) {
|
||||
mRootLayout.addView(createDotView());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (View view : statViews) {
|
||||
mRootLayout.addView(view);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mRootLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
for (View view : statViews) {
|
||||
mRootLayout.addView(view);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameDataExport.getInstance().isCapturing()) {
|
||||
String dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
|
||||
String pkgName = ForegroundAppDetector.getForegroundPackageName(mContext);
|
||||
|
||||
GameDataExport.getInstance().addOverlayData(
|
||||
dateTime,
|
||||
pkgName, // PackageName
|
||||
fpsStr, // FPS
|
||||
batteryTempStr, // Battery_Temp
|
||||
cpuUsageStr, // CPU_Usage
|
||||
cpuTempStr, // CPU_Temp
|
||||
gpuUsageStr, // GPU_Usage
|
||||
gpuClockStr, // GPU_Clock
|
||||
gpuTempStr // GPU_Temp
|
||||
);
|
||||
}
|
||||
|
||||
if (mLayoutParams != null) {
|
||||
mWindowManager.updateViewLayout(mOverlayView, mLayoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
private View buildCpuFreqView(List<String> freqs) {
|
||||
LinearLayout freqContainer = new LinearLayout(mContext);
|
||||
freqContainer.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
int spacingPx = dpToPx(mContext, mItemSpacingDp);
|
||||
LinearLayout.LayoutParams outerLp = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
outerLp.setMargins(spacingPx, spacingPx/2, spacingPx, spacingPx/2);
|
||||
freqContainer.setLayoutParams(outerLp);
|
||||
|
||||
if ("full".equals(mOverlayFormat)) {
|
||||
TextView labelTv = new TextView(mContext);
|
||||
labelTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
labelTv.setTextColor(Color.parseColor(mTitleColorHex));
|
||||
} catch (Exception e) {
|
||||
labelTv.setTextColor(Color.WHITE);
|
||||
}
|
||||
labelTv.setText("CPU Freq ");
|
||||
freqContainer.addView(labelTv);
|
||||
}
|
||||
|
||||
LinearLayout verticalFreqs = new LinearLayout(mContext);
|
||||
verticalFreqs.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
for (String freqLine : freqs) {
|
||||
LinearLayout lineLayout = new LinearLayout(mContext);
|
||||
lineLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
TextView freqTv = new TextView(mContext);
|
||||
freqTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
freqTv.setTextColor(Color.parseColor(mValueColorHex));
|
||||
} catch (Exception e) {
|
||||
freqTv.setTextColor(Color.WHITE);
|
||||
}
|
||||
freqTv.setText(freqLine);
|
||||
|
||||
lineLayout.addView(freqTv);
|
||||
|
||||
LinearLayout.LayoutParams lineLp = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
lineLp.setMargins(spacingPx, spacingPx/4, spacingPx, spacingPx/4);
|
||||
lineLayout.setLayoutParams(lineLp);
|
||||
|
||||
verticalFreqs.addView(lineLayout);
|
||||
}
|
||||
|
||||
freqContainer.addView(verticalFreqs);
|
||||
return freqContainer;
|
||||
}
|
||||
|
||||
private LinearLayout createStatLine(String title, String rawValue) {
|
||||
LinearLayout lineLayout = new LinearLayout(mContext);
|
||||
lineLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
if ("full".equals(mOverlayFormat)) {
|
||||
TextView tvTitle = new TextView(mContext);
|
||||
tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
tvTitle.setTextColor(Color.parseColor(mTitleColorHex));
|
||||
} catch (Exception e) {
|
||||
tvTitle.setTextColor(Color.WHITE);
|
||||
}
|
||||
tvTitle.setText(title.isEmpty() ? "" : title + " ");
|
||||
|
||||
TextView tvValue = new TextView(mContext);
|
||||
tvValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
tvValue.setTextColor(Color.parseColor(mValueColorHex));
|
||||
} catch (Exception e) {
|
||||
tvValue.setTextColor(Color.WHITE);
|
||||
}
|
||||
tvValue.setText(rawValue);
|
||||
|
||||
lineLayout.addView(tvTitle);
|
||||
lineLayout.addView(tvValue);
|
||||
} else {
|
||||
TextView tvMinimal = new TextView(mContext);
|
||||
tvMinimal.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
tvMinimal.setTextColor(Color.parseColor(mValueColorHex));
|
||||
} catch (Exception e) {
|
||||
tvMinimal.setTextColor(Color.WHITE);
|
||||
}
|
||||
tvMinimal.setText(rawValue);
|
||||
lineLayout.addView(tvMinimal);
|
||||
}
|
||||
|
||||
int spacingPx = dpToPx(mContext, mItemSpacingDp);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
lp.setMargins(spacingPx, spacingPx/2, spacingPx, spacingPx/2);
|
||||
lineLayout.setLayoutParams(lp);
|
||||
|
||||
return lineLayout;
|
||||
}
|
||||
|
||||
private View createDotView() {
|
||||
TextView dotView = new TextView(mContext);
|
||||
dotView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);
|
||||
try {
|
||||
dotView.setTextColor(Color.parseColor(mValueColorHex));
|
||||
} catch (Exception e) {
|
||||
dotView.setTextColor(Color.WHITE);
|
||||
}
|
||||
dotView.setText(" . ");
|
||||
return dotView;
|
||||
}
|
||||
|
||||
private float parseFps() {
|
||||
String line = readLine(FPS_PATH);
|
||||
if (line != null && line.startsWith("fps:")) {
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
return Float.parseFloat(parts[1].trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
public void setShowBatteryTemp(boolean show) { mShowBatteryTemp = show; }
|
||||
public void setShowCpuUsage(boolean show) { mShowCpuUsage = show; }
|
||||
public void setShowCpuClock(boolean show) { mShowCpuClock = show; }
|
||||
public void setShowCpuTemp(boolean show) { mShowCpuTemp = show; }
|
||||
public void setShowRam(boolean show) { mShowRam = show; }
|
||||
public void setShowFps(boolean show) { mShowFps = show; }
|
||||
|
||||
public void setShowGpuUsage(boolean show) { mShowGpuUsage = show; }
|
||||
public void setShowGpuClock(boolean show) { mShowGpuClock = show; }
|
||||
public void setShowGpuTemp(boolean show) { mShowGpuTemp = show; }
|
||||
|
||||
public void updateTextSize(int sp) {
|
||||
mTextSizeSp = sp;
|
||||
}
|
||||
|
||||
public void updateCornerRadius(int radius) {
|
||||
mCornerRadius = radius;
|
||||
applyBackgroundStyle();
|
||||
}
|
||||
|
||||
public void updateBackgroundAlpha(int alpha) {
|
||||
mBackgroundAlpha = alpha;
|
||||
applyBackgroundStyle();
|
||||
}
|
||||
|
||||
public void updatePadding(int dp) {
|
||||
mPaddingDp = dp;
|
||||
applyPadding();
|
||||
}
|
||||
|
||||
public void updateTitleColor(String hex) {
|
||||
mTitleColorHex = hex;
|
||||
}
|
||||
|
||||
public void updateValueColor(String hex) {
|
||||
mValueColorHex = hex;
|
||||
}
|
||||
|
||||
public void updateOverlayFormat(String format) {
|
||||
mOverlayFormat = format;
|
||||
if (mIsShowing) {
|
||||
updateStats();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateItemSpacing(int dp) {
|
||||
mItemSpacingDp = dp;
|
||||
if (mIsShowing) {
|
||||
updateStats();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyBackgroundStyle() {
|
||||
int color = Color.argb(mBackgroundAlpha, 0, 0, 0);
|
||||
mBgDrawable.setColor(color);
|
||||
mBgDrawable.setCornerRadius(mCornerRadius);
|
||||
|
||||
if (mOverlayView != null) {
|
||||
mOverlayView.setBackground(mBgDrawable);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPadding() {
|
||||
if (mRootLayout != null) {
|
||||
int px = dpToPx(mContext, mPaddingDp);
|
||||
mRootLayout.setPadding(px, px, px, px);
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePosition(String pos) {
|
||||
mPosition = pos;
|
||||
if (mIsShowing && mOverlayView != null && mLayoutParams != null) {
|
||||
if ("draggable".equals(mPosition)) {
|
||||
mDraggable = true;
|
||||
loadSavedPosition(mLayoutParams);
|
||||
if (mLayoutParams.x == 0 && mLayoutParams.y == 0) {
|
||||
mLayoutParams.gravity = Gravity.TOP | Gravity.START;
|
||||
mLayoutParams.x = 0;
|
||||
mLayoutParams.y = 100;
|
||||
}
|
||||
} else {
|
||||
mDraggable = false;
|
||||
applyPosition(mLayoutParams, mPosition);
|
||||
}
|
||||
mWindowManager.updateViewLayout(mOverlayView, mLayoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSplitMode(String mode) {
|
||||
mSplitMode = mode;
|
||||
if (mIsShowing && mOverlayView != null) {
|
||||
applySplitMode();
|
||||
updateStats();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUpdateInterval(String intervalStr) {
|
||||
try {
|
||||
mUpdateIntervalMs = Integer.parseInt(intervalStr);
|
||||
} catch (NumberFormatException e) {
|
||||
mUpdateIntervalMs = 1000;
|
||||
}
|
||||
if (mIsShowing) {
|
||||
startUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLongPressEnabled(boolean enabled) {
|
||||
mLongPressEnabled = enabled;
|
||||
}
|
||||
public void setLongPressThresholdMs(long ms) {
|
||||
mLongPressThresholdMs = ms;
|
||||
}
|
||||
|
||||
public void setDoubleTapCaptureEnabled(boolean enabled) {
|
||||
mDoubleTapCaptureEnabled = enabled;
|
||||
}
|
||||
|
||||
public void setSingleTapToggleEnabled(boolean enabled) {
|
||||
mSingleTapToggleEnabled = enabled;
|
||||
}
|
||||
|
||||
private void startUpdates() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
mHandler.post(mUpdateRunnable);
|
||||
}
|
||||
|
||||
private void applySplitMode() {
|
||||
if (mRootLayout == null) return;
|
||||
if ("side_by_side".equals(mSplitMode)) {
|
||||
mRootLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
} else {
|
||||
mRootLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSavedPosition(WindowManager.LayoutParams lp) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
int savedX = prefs.getInt(PREF_KEY_X, Integer.MIN_VALUE);
|
||||
int savedY = prefs.getInt(PREF_KEY_Y, Integer.MIN_VALUE);
|
||||
if (savedX != Integer.MIN_VALUE && savedY != Integer.MIN_VALUE) {
|
||||
lp.gravity = Gravity.TOP | Gravity.START;
|
||||
lp.x = savedX;
|
||||
lp.y = savedY;
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPosition(WindowManager.LayoutParams lp, String pos) {
|
||||
switch (pos) {
|
||||
case "top_left":
|
||||
lp.gravity = Gravity.TOP | Gravity.START;
|
||||
lp.x = 0;
|
||||
lp.y = 100;
|
||||
break;
|
||||
case "top_center":
|
||||
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
|
||||
lp.y = 100;
|
||||
break;
|
||||
case "top_right":
|
||||
lp.gravity = Gravity.TOP | Gravity.END;
|
||||
lp.x = 0;
|
||||
lp.y = 100;
|
||||
break;
|
||||
case "bottom_left":
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.START;
|
||||
lp.x = 0;
|
||||
lp.y = 100;
|
||||
break;
|
||||
case "bottom_center":
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
|
||||
lp.y = 100;
|
||||
break;
|
||||
case "bottom_right":
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.END;
|
||||
lp.x = 0;
|
||||
lp.y = 100;
|
||||
break;
|
||||
default:
|
||||
lp.gravity = Gravity.TOP | Gravity.START;
|
||||
lp.x = 0;
|
||||
lp.y = 100;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String readLine(String path) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
|
||||
return br.readLine();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void openOverlaySettings() {
|
||||
try {
|
||||
Intent intent = new Intent(mContext, GameOverlaySettingsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mContext.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static int dpToPx(Context context, int dp) {
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * scale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public class GameOverlayBootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|
||||
|| Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
|
||||
restoreOverlayState(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreOverlayState(Context context) {
|
||||
var prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean enabled = prefs.getBoolean("game_overlay_enable", false);
|
||||
GameOverlay overlay = GameOverlay.getInstance(context);
|
||||
if (!enabled) {
|
||||
overlay.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
overlay.applyPreferences();
|
||||
overlay.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class GameOverlayCpuInfo {
|
||||
|
||||
private static long sPrevIdle = -1;
|
||||
private static long sPrevTotal = -1;
|
||||
|
||||
private static final String CPU_TEMP_PATH = "/sys/class/thermal/thermal_zone0/temp";
|
||||
|
||||
public static String getCpuUsage() {
|
||||
String line = readLine("/proc/stat");
|
||||
if (line == null || !line.startsWith("cpu ")) return "N/A";
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length < 8) return "N/A";
|
||||
|
||||
try {
|
||||
long user = Long.parseLong(parts[1]);
|
||||
long nice = Long.parseLong(parts[2]);
|
||||
long system = Long.parseLong(parts[3]);
|
||||
long idle = Long.parseLong(parts[4]);
|
||||
long iowait = Long.parseLong(parts[5]);
|
||||
long irq = Long.parseLong(parts[6]);
|
||||
long softirq = Long.parseLong(parts[7]);
|
||||
long steal = parts.length > 8 ? Long.parseLong(parts[8]) : 0;
|
||||
|
||||
long total = user + nice + system + idle + iowait + irq + softirq + steal;
|
||||
|
||||
if (sPrevTotal != -1 && total != sPrevTotal) {
|
||||
long diffTotal = total - sPrevTotal;
|
||||
long diffIdle = idle - sPrevIdle;
|
||||
long usage = 100 * (diffTotal - diffIdle) / diffTotal;
|
||||
sPrevTotal = total;
|
||||
sPrevIdle = idle;
|
||||
return String.valueOf(usage);
|
||||
} else {
|
||||
|
||||
sPrevTotal = total;
|
||||
sPrevIdle = idle;
|
||||
return "N/A";
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getCpuFrequencies() {
|
||||
List<String> result = new ArrayList<>();
|
||||
String cpuDirPath = "/sys/devices/system/cpu/";
|
||||
java.io.File cpuDir = new java.io.File(cpuDirPath);
|
||||
java.io.File[] files = cpuDir.listFiles((dir, name) -> name.matches("cpu\\d+"));
|
||||
if (files == null || files.length == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
List<java.io.File> cpuFolders = new ArrayList<>();
|
||||
Collections.addAll(cpuFolders, files);
|
||||
cpuFolders.sort(Comparator.comparingInt(GameOverlayCpuInfo::extractCpuNumber));
|
||||
|
||||
for (java.io.File cpu : cpuFolders) {
|
||||
String freqPath = cpu.getAbsolutePath() + "/cpufreq/scaling_cur_freq";
|
||||
String freqStr = readLine(freqPath);
|
||||
if (freqStr != null && !freqStr.isEmpty()) {
|
||||
try {
|
||||
int khz = Integer.parseInt(freqStr.trim());
|
||||
int mhz = khz / 1000;
|
||||
result.add(cpu.getName() + ": " + mhz + " MHz");
|
||||
} catch (NumberFormatException e) {
|
||||
result.add(cpu.getName() + ": N/A");
|
||||
}
|
||||
} else {
|
||||
result.add(cpu.getName() + ": offline or frequency not available");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getCpuTemp() {
|
||||
String line = readLine(CPU_TEMP_PATH);
|
||||
if (line == null) return "N/A";
|
||||
line = line.trim();
|
||||
try {
|
||||
float raw = Float.parseFloat(line);
|
||||
float c = raw / 1000f;
|
||||
return String.format("%.1f", c);
|
||||
} catch (NumberFormatException e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
private static int extractCpuNumber(java.io.File cpuFolder) {
|
||||
String name = cpuFolder.getName().replace("cpu", "");
|
||||
try {
|
||||
return Integer.parseInt(name);
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static String readLine(String path) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
|
||||
return br.readLine();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SeekBarPreference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameOverlayFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private GameOverlay mOverlay;
|
||||
|
||||
private SwitchPreference mMasterSwitch;
|
||||
|
||||
private SwitchPreference mFpsSwitch;
|
||||
private SwitchPreference mBatteryTempSwitch;
|
||||
private SwitchPreference mCpuUsageSwitch;
|
||||
private SwitchPreference mCpuClockSwitch;
|
||||
private SwitchPreference mCpuTempSwitch;
|
||||
private SwitchPreference mRamSwitch;
|
||||
|
||||
private SwitchPreference mGpuUsageSwitch;
|
||||
private SwitchPreference mGpuClockSwitch;
|
||||
private SwitchPreference mGpuTempSwitch;
|
||||
|
||||
private Preference mCaptureStartPref;
|
||||
private Preference mCaptureStopPref;
|
||||
private Preference mCaptureExportPref;
|
||||
|
||||
private SwitchPreference mDoubleTapCapturePref;
|
||||
private SwitchPreference mSingleTapTogglePref;
|
||||
private SwitchPreference mLongPressEnablePref;
|
||||
private ListPreference mLongPressTimeoutPref;
|
||||
|
||||
private SeekBarPreference mTextSizePref;
|
||||
private SeekBarPreference mBgAlphaPref;
|
||||
private SeekBarPreference mCornerRadiusPref;
|
||||
private SeekBarPreference mPaddingPref;
|
||||
private SeekBarPreference mItemSpacingPref;
|
||||
|
||||
private ListPreference mUpdateIntervalPref;
|
||||
private ListPreference mTextColorPref;
|
||||
private ListPreference mTitleColorPref;
|
||||
private ListPreference mValueColorPref;
|
||||
private ListPreference mPositionPref;
|
||||
private ListPreference mSplitModePref;
|
||||
private ListPreference mOverlayFormatPref;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.game_overlay_preferences, rootKey);
|
||||
|
||||
mOverlay = GameOverlay.getInstance(getContext());
|
||||
|
||||
mMasterSwitch = findPreference("game_overlay_enable");
|
||||
|
||||
mFpsSwitch = findPreference("game_overlay_fps_enable");
|
||||
mBatteryTempSwitch = findPreference("game_overlay_temp_enable");
|
||||
mCpuUsageSwitch = findPreference("game_overlay_cpu_usage_enable");
|
||||
mCpuClockSwitch = findPreference("game_overlay_cpu_clock_enable");
|
||||
mCpuTempSwitch = findPreference("game_overlay_cpu_temp_enable");
|
||||
mRamSwitch = findPreference("game_overlay_ram_enable");
|
||||
|
||||
mGpuUsageSwitch = findPreference("game_overlay_gpu_usage_enable");
|
||||
mGpuClockSwitch = findPreference("game_overlay_gpu_clock_enable");
|
||||
mGpuTempSwitch = findPreference("game_overlay_gpu_temp_enable");
|
||||
|
||||
mCaptureStartPref = findPreference("game_overlay_capture_start");
|
||||
mCaptureStopPref = findPreference("game_overlay_capture_stop");
|
||||
mCaptureExportPref = findPreference("game_overlay_capture_export");
|
||||
|
||||
mDoubleTapCapturePref = findPreference("game_overlay_doubletap_capture");
|
||||
mSingleTapTogglePref = findPreference("game_overlay_single_tap_toggle");
|
||||
mLongPressEnablePref = findPreference("game_overlay_longpress_enable");
|
||||
mLongPressTimeoutPref = findPreference("game_overlay_longpress_timeout");
|
||||
|
||||
mTextSizePref = findPreference("game_overlay_text_size");
|
||||
mBgAlphaPref = findPreference("game_overlay_background_alpha");
|
||||
mCornerRadiusPref = findPreference("game_overlay_corner_radius");
|
||||
mPaddingPref = findPreference("game_overlay_padding");
|
||||
mItemSpacingPref = findPreference("game_overlay_item_spacing");
|
||||
|
||||
mUpdateIntervalPref = findPreference("game_overlay_update_interval");
|
||||
mTextColorPref = findPreference("game_overlay_text_color");
|
||||
mTitleColorPref = findPreference("game_overlay_title_color");
|
||||
mValueColorPref = findPreference("game_overlay_value_color");
|
||||
mPositionPref = findPreference("game_overlay_position");
|
||||
mSplitModePref = findPreference("game_overlay_split_mode");
|
||||
mOverlayFormatPref = findPreference("game_overlay_format");
|
||||
|
||||
if (mMasterSwitch != null) {
|
||||
mMasterSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
boolean enabled = (boolean) newValue;
|
||||
if (enabled) {
|
||||
if (Settings.canDrawOverlays(getContext())) {
|
||||
mOverlay.applyPreferences();
|
||||
mOverlay.show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.overlay_permission_required, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mOverlay.hide();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mFpsSwitch != null) {
|
||||
mFpsSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowFps((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mBatteryTempSwitch != null) {
|
||||
mBatteryTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowBatteryTemp((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuUsageSwitch != null) {
|
||||
mCpuUsageSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowCpuUsage((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuClockSwitch != null) {
|
||||
mCpuClockSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowCpuClock((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuTempSwitch != null) {
|
||||
mCpuTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowCpuTemp((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mRamSwitch != null) {
|
||||
mRamSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowRam((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mGpuUsageSwitch != null) {
|
||||
mGpuUsageSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowGpuUsage((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mGpuClockSwitch != null) {
|
||||
mGpuClockSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowGpuClock((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mGpuTempSwitch != null) {
|
||||
mGpuTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setShowGpuTemp((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mCaptureStartPref != null) {
|
||||
mCaptureStartPref.setOnPreferenceClickListener(pref -> {
|
||||
GameDataExport.getInstance().startCapture();
|
||||
Toast.makeText(getContext(), "Started logging Data", Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCaptureStopPref != null) {
|
||||
mCaptureStopPref.setOnPreferenceClickListener(pref -> {
|
||||
GameDataExport.getInstance().stopCapture();
|
||||
Toast.makeText(getContext(), "Stopped logging Data", Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCaptureExportPref != null) {
|
||||
mCaptureExportPref.setOnPreferenceClickListener(pref -> {
|
||||
GameDataExport.getInstance().exportDataToCsv();
|
||||
Toast.makeText(getContext(), "Exported log data to file", Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mDoubleTapCapturePref != null) {
|
||||
mDoubleTapCapturePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setDoubleTapCaptureEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mSingleTapTogglePref != null) {
|
||||
mSingleTapTogglePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setSingleTapToggleEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mLongPressEnablePref != null) {
|
||||
mLongPressEnablePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mOverlay.setLongPressEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mLongPressTimeoutPref != null) {
|
||||
mLongPressTimeoutPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
long ms = Long.parseLong((String) newValue);
|
||||
mOverlay.setLongPressThresholdMs(ms);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mTextSizePref != null) {
|
||||
mTextSizePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mOverlay.updateTextSize((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mBgAlphaPref != null) {
|
||||
mBgAlphaPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mOverlay.updateBackgroundAlpha((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCornerRadiusPref != null) {
|
||||
mCornerRadiusPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mOverlay.updateCornerRadius((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mPaddingPref != null) {
|
||||
mPaddingPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mOverlay.updatePadding((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mItemSpacingPref != null) {
|
||||
mItemSpacingPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mOverlay.updateItemSpacing((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mUpdateIntervalPref != null) {
|
||||
mUpdateIntervalPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updateUpdateInterval((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mTextColorPref != null) {
|
||||
mTextColorPref.setOnPreferenceChangeListener((pref, newValue) -> true);
|
||||
}
|
||||
if (mTitleColorPref != null) {
|
||||
mTitleColorPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updateTitleColor((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mValueColorPref != null) {
|
||||
mValueColorPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updateValueColor((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mPositionPref != null) {
|
||||
mPositionPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updatePosition((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mSplitModePref != null) {
|
||||
mSplitModePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updateSplitMode((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mOverlayFormatPref != null) {
|
||||
mOverlayFormatPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mOverlay.updateOverlayFormat((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!hasUsageStatsPermission(requireContext())) {
|
||||
requestUsageStatsPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasUsageStatsPermission(Context context) {
|
||||
android.app.AppOpsManager appOps = (android.app.AppOpsManager)
|
||||
context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
if (appOps == null) return false;
|
||||
int mode = appOps.checkOpNoThrow(
|
||||
android.app.AppOpsManager.OPSTR_GET_USAGE_STATS,
|
||||
android.os.Process.myUid(),
|
||||
context.getPackageName()
|
||||
);
|
||||
return (mode == android.app.AppOpsManager.MODE_ALLOWED);
|
||||
}
|
||||
|
||||
private void requestUsageStatsPermission() {
|
||||
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GameOverlayGpuInfo {
|
||||
|
||||
private static final String GPU_USAGE_PATH = "/sys/class/kgsl/kgsl-3d0/gpu_busy_percentage";
|
||||
private static final String GPU_CLOCK_PATH = "/sys/class/kgsl/kgsl-3d0/gpuclk";
|
||||
private static final String GPU_TEMP_PATH = "/sys/class/kgsl/kgsl-3d0/temp";
|
||||
|
||||
public static String getGpuUsage() {
|
||||
String line = readLine(GPU_USAGE_PATH);
|
||||
if (line == null) {
|
||||
return "N/A";
|
||||
}
|
||||
line = line.replace("%", "").trim();
|
||||
try {
|
||||
int val = Integer.parseInt(line);
|
||||
return String.valueOf(val);
|
||||
} catch (NumberFormatException e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getGpuClock() {
|
||||
String line = readLine(GPU_CLOCK_PATH);
|
||||
if (line == null) {
|
||||
return "N/A";
|
||||
}
|
||||
line = line.trim();
|
||||
try {
|
||||
long hz = Long.parseLong(line);
|
||||
long mhz = hz / 1_000_000;
|
||||
return String.valueOf(mhz);
|
||||
} catch (NumberFormatException e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getGpuTemp() {
|
||||
String line = readLine(GPU_TEMP_PATH);
|
||||
if (line == null) {
|
||||
return "N/A";
|
||||
}
|
||||
line = line.trim();
|
||||
try {
|
||||
float raw = Float.parseFloat(line);
|
||||
float c = raw / 1000f;
|
||||
return String.format("%.1f", c);
|
||||
} catch (NumberFormatException e) {
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
private static String readLine(String path) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
|
||||
return br.readLine();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GameOverlayMemInfo {
|
||||
|
||||
public static String getRamUsage() {
|
||||
long memTotal = 0;
|
||||
long memAvailable = 0;
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.startsWith("MemTotal:")) {
|
||||
memTotal = parseMemValue(line);
|
||||
} else if (line.startsWith("MemAvailable:")) {
|
||||
memAvailable = parseMemValue(line);
|
||||
}
|
||||
if (memTotal > 0 && memAvailable > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
if (memTotal == 0) {
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
long usedKb = (memTotal - memAvailable);
|
||||
long usedMb = usedKb / 1024;
|
||||
return String.valueOf(usedMb);
|
||||
}
|
||||
|
||||
private static long parseMemValue(String line) {
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length < 3) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(parts[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameOverlaySettingsActivity extends CollapsingToolbarBaseActivity {
|
||||
private static final int OVERLAY_PERMISSION_REQUEST_CODE = 1234;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game_overlay);
|
||||
setTitle(getString(R.string.game_overlay_title));
|
||||
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
Toast.makeText(this, R.string.overlay_permission_granted, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.overlay_permission_denied, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2025 kenway214
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.gameoverlay;
|
||||
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameOverlayTileService extends TileService {
|
||||
private GameOverlay mOverlay;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mOverlay = GameOverlay.getInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
super.onStartListening();
|
||||
boolean enabled = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("game_overlay_enable", false);
|
||||
updateTileState(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
super.onClick();
|
||||
boolean currentlyEnabled = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("game_overlay_enable", false);
|
||||
boolean newState = !currentlyEnabled;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.edit()
|
||||
.putBoolean("game_overlay_enable", newState)
|
||||
.commit();
|
||||
|
||||
updateTileState(newState);
|
||||
|
||||
if (newState) {
|
||||
mOverlay.show();
|
||||
} else {
|
||||
mOverlay.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTileState(boolean enabled) {
|
||||
Tile tile = getQsTile();
|
||||
if (tile == null) return;
|
||||
|
||||
tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
|
||||
tile.setLabel(getString(R.string.game_overlay_tile_label));
|
||||
tile.setContentDescription(getString(R.string.game_overlay_tile_description));
|
||||
tile.updateTile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2022 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.refreshrate;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
public class RefreshActivity extends CollapsingToolbarBaseActivity {
|
||||
private static final String TAG_REFRESH = "refresh";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
new RefreshSettingsFragment(), TAG_REFRESH).commit();
|
||||
}
|
||||
}
|
||||
102
parts/src/org/lineageos/settings/refreshrate/RefreshService.java
Normal file
102
parts/src/org/lineageos/settings/refreshrate/RefreshService.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.refreshrate;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.ActivityTaskManager.RootTaskInfo;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.app.TaskStackListener;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.os.RemoteException;
|
||||
|
||||
public class RefreshService extends Service {
|
||||
|
||||
private static final String TAG = "RefreshService";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private String mPreviousApp;
|
||||
private RefreshUtils mRefreshUtils;
|
||||
private IActivityTaskManager mActivityTaskManager;
|
||||
|
||||
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mPreviousApp = "";
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (DEBUG) Log.d(TAG, "Creating service");
|
||||
try {
|
||||
mActivityTaskManager = ActivityTaskManager.getService();
|
||||
mActivityTaskManager.registerTaskStackListener(mTaskListener);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing
|
||||
}
|
||||
mRefreshUtils = new RefreshUtils(this);
|
||||
registerReceiver();
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (DEBUG) Log.d(TAG, "Starting service");
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerReceiver() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
this.registerReceiver(mIntentReceiver, filter);
|
||||
}
|
||||
|
||||
private final TaskStackListener mTaskListener = new TaskStackListener() {
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
try {
|
||||
final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo();
|
||||
if (info == null || info.topActivity == null) {
|
||||
return;
|
||||
}
|
||||
String foregroundApp = info.topActivity.getPackageName();
|
||||
if (!mRefreshUtils.isAppInList) {
|
||||
mRefreshUtils.getOldRate();
|
||||
}
|
||||
if (!foregroundApp.equals(mPreviousApp)) {
|
||||
mRefreshUtils.setRefreshRate(foregroundApp);
|
||||
mPreviousApp = foregroundApp;
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
/**
|
||||
* Copyright (C) 2020 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.lineageos.settings.refreshrate;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SectionIndexer;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RefreshSettingsFragment extends PreferenceFragment
|
||||
implements ApplicationsState.Callbacks {
|
||||
|
||||
private AllPackagesAdapter mAllPackagesAdapter;
|
||||
private ApplicationsState mApplicationsState;
|
||||
private ApplicationsState.Session mSession;
|
||||
private ActivityFilter mActivityFilter;
|
||||
private Map<String, ApplicationsState.AppEntry> mEntryMap =
|
||||
new HashMap<String, ApplicationsState.AppEntry>();
|
||||
|
||||
private RefreshUtils mRefreshUtils;
|
||||
private RecyclerView mAppsRecyclerView;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
|
||||
mSession = mApplicationsState.newSession(this);
|
||||
mSession.onResume();
|
||||
mActivityFilter = new ActivityFilter(getActivity().getPackageManager());
|
||||
|
||||
mAllPackagesAdapter = new AllPackagesAdapter(getActivity());
|
||||
|
||||
mRefreshUtils = new RefreshUtils(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.refresh_layout, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mAppsRecyclerView = view.findViewById(R.id.refresh_rv_view);
|
||||
mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mAppsRecyclerView.setAdapter(mAllPackagesAdapter);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getActivity().setTitle(getResources().getString(R.string.refresh_title));
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
mSession.onPause();
|
||||
mSession.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
mActivityFilter.updateLauncherInfoList();
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> entries) {
|
||||
if (entries != null) {
|
||||
handleAppEntries(entries);
|
||||
mAllPackagesAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEntriesCompleted() {
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllSizesComputed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLauncherInfoChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageIconChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageSizeChanged(String packageName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningStateChanged(boolean running) {
|
||||
}
|
||||
|
||||
private void handleAppEntries(List<ApplicationsState.AppEntry> entries) {
|
||||
final ArrayList<String> sections = new ArrayList<String>();
|
||||
final ArrayList<Integer> positions = new ArrayList<Integer>();
|
||||
final PackageManager pm = getActivity().getPackageManager();
|
||||
String lastSectionIndex = null;
|
||||
int offset = 0;
|
||||
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
final ApplicationInfo info = entries.get(i).info;
|
||||
final String label = (String) info.loadLabel(pm);
|
||||
final String sectionIndex;
|
||||
|
||||
if (!info.enabled) {
|
||||
sectionIndex = "--"; // XXX
|
||||
} else if (TextUtils.isEmpty(label)) {
|
||||
sectionIndex = "";
|
||||
} else {
|
||||
sectionIndex = label.substring(0, 1).toUpperCase();
|
||||
}
|
||||
|
||||
if (lastSectionIndex == null ||
|
||||
!TextUtils.equals(sectionIndex, lastSectionIndex)) {
|
||||
sections.add(sectionIndex);
|
||||
positions.add(offset);
|
||||
lastSectionIndex = sectionIndex;
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
mAllPackagesAdapter.setEntries(entries, sections, positions);
|
||||
mEntryMap.clear();
|
||||
for (ApplicationsState.AppEntry e : entries) {
|
||||
mEntryMap.put(e.info.packageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuild() {
|
||||
mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR);
|
||||
}
|
||||
|
||||
private int getStateDrawable(int state) {
|
||||
switch (state) {
|
||||
case RefreshUtils.STATE_STANDARD:
|
||||
return R.drawable.ic_refresh_60;
|
||||
case RefreshUtils.STATE_EXTREME:
|
||||
return R.drawable.ic_refresh_120;
|
||||
case RefreshUtils.STATE_DEFAULT:
|
||||
default:
|
||||
return R.drawable.ic_refresh_default;
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private TextView title;
|
||||
private Spinner mode;
|
||||
private ImageView icon;
|
||||
private View rootView;
|
||||
private ImageView stateIcon;
|
||||
|
||||
private ViewHolder(View view) {
|
||||
super(view);
|
||||
this.title = view.findViewById(R.id.app_name);
|
||||
this.mode = view.findViewById(R.id.app_mode);
|
||||
this.icon = view.findViewById(R.id.app_icon);
|
||||
this.stateIcon = view.findViewById(R.id.state);
|
||||
this.rootView = view;
|
||||
|
||||
view.setTag(this);
|
||||
}
|
||||
}
|
||||
|
||||
private class ModeAdapter extends BaseAdapter {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final int[] items = {
|
||||
R.string.refresh_default,
|
||||
R.string.refresh_standard,
|
||||
R.string.refresh_extreme
|
||||
};
|
||||
|
||||
private ModeAdapter(Context context) {
|
||||
inflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return items[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView view;
|
||||
if (convertView != null) {
|
||||
view = (TextView) convertView;
|
||||
} else {
|
||||
view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
view.setText(items[position]);
|
||||
view.setTextSize(14f);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
private class AllPackagesAdapter extends RecyclerView.Adapter<ViewHolder>
|
||||
implements AdapterView.OnItemSelectedListener, SectionIndexer {
|
||||
|
||||
private List<ApplicationsState.AppEntry> mEntries = new ArrayList<>();
|
||||
private String[] mSections;
|
||||
private int[] mPositions;
|
||||
|
||||
public AllPackagesAdapter(Context context) {
|
||||
mActivityFilter = new ActivityFilter(context.getPackageManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mEntries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mEntries.get(position).id;
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.refresh_list_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
Context context = holder.itemView.getContext();
|
||||
|
||||
ApplicationsState.AppEntry entry = mEntries.get(position);
|
||||
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
holder.mode.setAdapter(new ModeAdapter(context));
|
||||
holder.mode.setOnItemSelectedListener(this);
|
||||
holder.title.setText(entry.label);
|
||||
holder.title.setOnClickListener(v -> holder.mode.performClick());
|
||||
mApplicationsState.ensureIcon(entry);
|
||||
holder.icon.setImageDrawable(entry.icon);
|
||||
int packageState = mRefreshUtils.getStateForPackage(entry.info.packageName);
|
||||
holder.mode.setSelection(packageState, false);
|
||||
holder.mode.setTag(entry);
|
||||
holder.stateIcon.setImageResource(getStateDrawable(packageState));
|
||||
}
|
||||
|
||||
private void setEntries(List<ApplicationsState.AppEntry> entries,
|
||||
List<String> sections, List<Integer> positions) {
|
||||
mEntries = entries;
|
||||
mSections = sections.toArray(new String[sections.size()]);
|
||||
mPositions = new int[positions.size()];
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
mPositions[i] = positions.get(i);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag();
|
||||
|
||||
int currentState = mRefreshUtils.getStateForPackage(entry.info.packageName);
|
||||
if (currentState != position) {
|
||||
mRefreshUtils.writePackage(entry.info.packageName, position);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionForSection(int section) {
|
||||
if (section < 0 || section >= mSections.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mPositions[section];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionForPosition(int position) {
|
||||
if (position < 0 || position >= getItemCount()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int index = Arrays.binarySearch(mPositions, position);
|
||||
|
||||
/*
|
||||
* Consider this example: section positions are 0, 3, 5; the supplied
|
||||
* position is 4. The section corresponding to position 4 starts at
|
||||
* position 3, so the expected return value is 1. Binary search will not
|
||||
* find 4 in the array and thus will return -insertPosition-1, i.e. -3.
|
||||
* To get from that number to the expected value of 1 we need to negate
|
||||
* and subtract 2.
|
||||
*/
|
||||
return index >= 0 ? index : -index - 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections() {
|
||||
return mSections;
|
||||
}
|
||||
}
|
||||
|
||||
private class ActivityFilter implements ApplicationsState.AppFilter {
|
||||
|
||||
private final PackageManager mPackageManager;
|
||||
private final List<String> mLauncherResolveInfoList = new ArrayList<String>();
|
||||
|
||||
private ActivityFilter(PackageManager packageManager) {
|
||||
this.mPackageManager = packageManager;
|
||||
|
||||
updateLauncherInfoList();
|
||||
}
|
||||
|
||||
public void updateLauncherInfoList() {
|
||||
Intent i = new Intent(Intent.ACTION_MAIN);
|
||||
i.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
List<ResolveInfo> resolveInfoList = mPackageManager.queryIntentActivities(i, 0);
|
||||
|
||||
synchronized (mLauncherResolveInfoList) {
|
||||
mLauncherResolveInfoList.clear();
|
||||
for (ResolveInfo ri : resolveInfoList) {
|
||||
mLauncherResolveInfoList.add(ri.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterApp(ApplicationsState.AppEntry entry) {
|
||||
boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName);
|
||||
if (show) {
|
||||
synchronized (mLauncherResolveInfoList) {
|
||||
show = mLauncherResolveInfoList.contains(entry.info.packageName);
|
||||
}
|
||||
}
|
||||
return show;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2021 crDroid Android Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.refreshrate;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.view.Display;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class RefreshTileService extends TileService {
|
||||
private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate";
|
||||
private static final String KEY_PREFERRED_REFRESH_RATE = "preferred_refresh_rate";
|
||||
private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate";
|
||||
|
||||
private Context context;
|
||||
private Tile tile;
|
||||
|
||||
private final List<Float> availableRates = new ArrayList<>();
|
||||
private int activeRateMin;
|
||||
private int activeRateMax;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
context = getApplicationContext();
|
||||
Display.Mode mode = context.getDisplay().getMode();
|
||||
Display.Mode[] modes = context.getDisplay().getSupportedModes();
|
||||
for (Display.Mode m : modes) {
|
||||
float rate = Float.valueOf(String.format(Locale.US, "%.02f", m.getRefreshRate()));
|
||||
if (m.getPhysicalWidth() == mode.getPhysicalWidth() &&
|
||||
m.getPhysicalHeight() == mode.getPhysicalHeight()) {
|
||||
availableRates.add(rate);
|
||||
}
|
||||
}
|
||||
syncFromSettings();
|
||||
}
|
||||
|
||||
private int getSettingOf(String key) {
|
||||
float rate = Settings.System.getFloat(context.getContentResolver(), key, 60);
|
||||
return availableRates.indexOf(
|
||||
Float.valueOf(String.format(Locale.US, "%.02f", rate)));
|
||||
}
|
||||
|
||||
private void syncFromSettings() {
|
||||
activeRateMin = getSettingOf(KEY_MIN_REFRESH_RATE);
|
||||
activeRateMax = getSettingOf(KEY_PEAK_REFRESH_RATE);
|
||||
}
|
||||
|
||||
private void cycleRefreshRate() {
|
||||
if (activeRateMin < availableRates.size() - 1) {
|
||||
activeRateMin++;
|
||||
} else {
|
||||
activeRateMin = 0;
|
||||
}
|
||||
|
||||
float rate = availableRates.get(activeRateMin);
|
||||
Settings.System.putFloat(context.getContentResolver(), KEY_MIN_REFRESH_RATE, rate);
|
||||
Settings.System.putFloat(context.getContentResolver(), KEY_PREFERRED_REFRESH_RATE, rate);
|
||||
Settings.System.putFloat(context.getContentResolver(), KEY_PEAK_REFRESH_RATE, rate);
|
||||
}
|
||||
|
||||
private String getFormatRate(float rate) {
|
||||
return String.format("%.02f Hz", rate)
|
||||
.replaceAll("[\\.,]00", "");
|
||||
}
|
||||
|
||||
private void updateTileView() {
|
||||
String displayText;
|
||||
float min = availableRates.get(activeRateMin);
|
||||
float max = availableRates.get(activeRateMax);
|
||||
|
||||
displayText = String.format(Locale.US, min == max ? "%s" : "%s - %s",
|
||||
getFormatRate(min), getFormatRate(max));
|
||||
tile.setContentDescription(displayText);
|
||||
tile.setSubtitle(displayText);
|
||||
tile.setState(min == max ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
|
||||
tile.updateTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
super.onStartListening();
|
||||
tile = getQsTile();
|
||||
syncFromSettings();
|
||||
updateTileView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
super.onClick();
|
||||
cycleRefreshRate();
|
||||
syncFromSettings();
|
||||
updateTileView();
|
||||
}
|
||||
}
|
||||
141
parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java
Normal file
141
parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.refreshrate;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.UserHandle;
|
||||
import android.view.Display;
|
||||
|
||||
import android.provider.Settings;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public final class RefreshUtils {
|
||||
|
||||
private static final String REFRESH_CONTROL = "refresh_control";
|
||||
|
||||
private static float defaultMaxRate;
|
||||
private static float defaultMinRate;
|
||||
private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate";
|
||||
private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate";
|
||||
private Context mContext;
|
||||
protected static boolean isAppInList = false;
|
||||
|
||||
protected static final int STATE_DEFAULT = 0;
|
||||
protected static final int STATE_STANDARD = 1;
|
||||
protected static final int STATE_EXTREME = 2;
|
||||
|
||||
private static final float REFRESH_STATE_DEFAULT = 120f;
|
||||
private static final float REFRESH_STATE_STANDARD = 60f;
|
||||
private static final float REFRESH_STATE_EXTREME = 120f;
|
||||
|
||||
private static final String REFRESH_STANDARD = "refresh.standard=";
|
||||
private static final String REFRESH_EXTREME = "refresh.extreme=";
|
||||
|
||||
private SharedPreferences mSharedPrefs;
|
||||
|
||||
protected RefreshUtils(Context context) {
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static void startService(Context context) {
|
||||
context.startServiceAsUser(new Intent(context, RefreshService.class),
|
||||
UserHandle.CURRENT);
|
||||
}
|
||||
|
||||
private void writeValue(String profiles) {
|
||||
mSharedPrefs.edit().putString(REFRESH_CONTROL, profiles).apply();
|
||||
}
|
||||
|
||||
protected void getOldRate(){
|
||||
defaultMaxRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, REFRESH_STATE_DEFAULT);
|
||||
defaultMinRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, REFRESH_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
private String getValue() {
|
||||
String value = mSharedPrefs.getString(REFRESH_CONTROL, null);
|
||||
|
||||
if (value == null || value.isEmpty()) {
|
||||
value = REFRESH_STANDARD + ":" + REFRESH_EXTREME;
|
||||
writeValue(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void writePackage(String packageName, int mode) {
|
||||
String value = getValue();
|
||||
value = value.replace(packageName + ",", "");
|
||||
String[] modes = value.split(":");
|
||||
String finalString;
|
||||
|
||||
switch (mode) {
|
||||
case STATE_STANDARD:
|
||||
modes[0] = modes[0] + packageName + ",";
|
||||
break;
|
||||
case STATE_EXTREME:
|
||||
modes[1] = modes[1] + packageName + ",";
|
||||
break;
|
||||
}
|
||||
|
||||
finalString = modes[0] + ":" + modes[1];
|
||||
|
||||
writeValue(finalString);
|
||||
}
|
||||
|
||||
protected int getStateForPackage(String packageName) {
|
||||
String value = getValue();
|
||||
String[] modes = value.split(":");
|
||||
int state = STATE_DEFAULT;
|
||||
if (modes[0].contains(packageName + ",")) {
|
||||
state = STATE_STANDARD;
|
||||
} else if (modes[1].contains(packageName + ",")) {
|
||||
state = STATE_EXTREME;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
protected void setRefreshRate(String packageName) {
|
||||
String value = getValue();
|
||||
String modes[];
|
||||
float maxrate = defaultMaxRate;
|
||||
float minrate = defaultMinRate;
|
||||
isAppInList = false;
|
||||
|
||||
if (value != null) {
|
||||
modes = value.split(":");
|
||||
|
||||
if (modes[0].contains(packageName + ",")) {
|
||||
maxrate = REFRESH_STATE_STANDARD;
|
||||
if ( minrate > maxrate){
|
||||
minrate = maxrate;
|
||||
}
|
||||
isAppInList = true;
|
||||
} else if (modes[1].contains(packageName + ",")) {
|
||||
maxrate = REFRESH_STATE_EXTREME;
|
||||
if ( minrate > maxrate){
|
||||
minrate = maxrate;
|
||||
}
|
||||
isAppInList = true;
|
||||
}
|
||||
}
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, minrate);
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, maxrate);
|
||||
}
|
||||
}
|
||||
47
parts/src/org/lineageos/settings/utils/ComponentUtils.java
Normal file
47
parts/src/org/lineageos/settings/utils/ComponentUtils.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.utils;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
public class ComponentUtils {
|
||||
|
||||
/**
|
||||
* Enables or disables a specified Android component dynamically at runtime.
|
||||
*
|
||||
* @param context The context from which the component will be enabled or disabled.
|
||||
* @param componentClass The class of the component to be enabled or disabled.
|
||||
* @param enable If true, the component will be enabled; if false, it will be disabled.
|
||||
*/
|
||||
public static void toggleComponent(Context context, Class<?> componentClass, boolean enable) {
|
||||
ComponentName componentName = new ComponentName(context, componentClass);
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
int currentState = packageManager.getComponentEnabledSetting(componentName);
|
||||
int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
||||
|
||||
if (currentState != newState) {
|
||||
packageManager.setComponentEnabledSetting(
|
||||
componentName,
|
||||
newState,
|
||||
PackageManager.DONT_KILL_APP
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
222
parts/src/org/lineageos/settings/utils/FileUtils.java
Normal file
222
parts/src/org/lineageos/settings/utils/FileUtils.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The CyanogenMod Project
|
||||
* 2025 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public final class FileUtils {
|
||||
private static final String TAG = "FileUtils";
|
||||
|
||||
private FileUtils() {
|
||||
// This class is not supposed to be instantiated
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first line of text from the given file.
|
||||
* Reference {@link BufferedReader#readLine()} for clarification on what a line is
|
||||
*
|
||||
* @return the read line contents, or null on failure
|
||||
*/
|
||||
public static String readOneLine(String fileName) {
|
||||
String line = null;
|
||||
BufferedReader reader = null;
|
||||
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(fileName), 512);
|
||||
line = reader.readLine();
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "No such file " + fileName + " for reading", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Could not read from file " + fileName, e);
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignored, not much we can do anyway
|
||||
}
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given value into the given file
|
||||
*
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
public static boolean writeLine(String fileName, String value) {
|
||||
BufferedWriter writer = null;
|
||||
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(fileName));
|
||||
writer.write(value);
|
||||
writer.flush();
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "No such file " + fileName + " for writing", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Could not write to file " + fileName, e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignored, not much we can do anyway
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given file exists
|
||||
*
|
||||
* @return true if exists, false if not
|
||||
*/
|
||||
public static boolean fileExists(String fileName) {
|
||||
final File file = new File(fileName);
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given file is readable
|
||||
*
|
||||
* @return true if readable, false if not
|
||||
*/
|
||||
public static boolean isFileReadable(String fileName) {
|
||||
final File file = new File(fileName);
|
||||
return file.exists() && file.canRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given file is writable
|
||||
*
|
||||
* @return true if writable, false if not
|
||||
*/
|
||||
public static boolean isFileWritable(String fileName) {
|
||||
final File file = new File(fileName);
|
||||
return file.exists() && file.canWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing file
|
||||
*
|
||||
* @return true if the delete was successful, false if not
|
||||
*/
|
||||
public static boolean delete(String fileName) {
|
||||
final File file = new File(fileName);
|
||||
boolean ok = false;
|
||||
try {
|
||||
ok = file.delete();
|
||||
} catch (SecurityException e) {
|
||||
Log.w(TAG, "SecurityException trying to delete " + fileName, e);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames an existing file
|
||||
*
|
||||
* @return true if the rename was successful, false if not
|
||||
*/
|
||||
public static boolean rename(String srcPath, String dstPath) {
|
||||
final File srcFile = new File(srcPath);
|
||||
final File dstFile = new File(dstPath);
|
||||
boolean ok = false;
|
||||
try {
|
||||
ok = srcFile.renameTo(dstFile);
|
||||
} catch (SecurityException e) {
|
||||
Log.w(TAG, "SecurityException trying to rename " + srcPath + " to " + dstPath, e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given value into the given file.
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
public static boolean writeValue(String fileName, String value) {
|
||||
BufferedWriter writer = null;
|
||||
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(fileName));
|
||||
writer.write(value);
|
||||
writer.flush();
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "No such file " + fileName + " for writing", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Could not write to file " + fileName, e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignored, not much we can do anyway
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value from the given file.
|
||||
* @return the value read from the file, or the default value if an error occurs
|
||||
*/
|
||||
public static String getFileValue(String fileName, String defaultValue) {
|
||||
String value = defaultValue;
|
||||
BufferedReader reader = null;
|
||||
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(fileName), 512);
|
||||
value = reader.readLine();
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "No such file " + fileName + " for reading", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Could not read from file " + fileName, e);
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignored, not much we can do anyway
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
62
parts/src/org/lineageos/settings/utils/TileUtils.java
Normal file
62
parts/src/org/lineageos/settings/utils/TileUtils.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.settings.utils;
|
||||
|
||||
import android.app.StatusBarManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class TileUtils {
|
||||
|
||||
public static void requestAddTileService(Context context, Class<?> tileServiceClass, int labelResId, int iconResId) {
|
||||
ComponentName componentName = new ComponentName(context, tileServiceClass);
|
||||
String label = context.getString(labelResId);
|
||||
Icon icon = Icon.createWithResource(context, iconResId);
|
||||
|
||||
StatusBarManager sbm = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
|
||||
|
||||
if (sbm != null) {
|
||||
sbm.requestAddTileService(
|
||||
componentName,
|
||||
label,
|
||||
icon,
|
||||
context.getMainExecutor(),
|
||||
result -> handleResult(context, result)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleResult(Context context, Integer result) {
|
||||
if (result == null)
|
||||
return;
|
||||
switch (result) {
|
||||
case StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED:
|
||||
Toast.makeText(context, R.string.tile_added, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED:
|
||||
Toast.makeText(context, R.string.tile_not_added, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED:
|
||||
Toast.makeText(context, R.string.tile_already_added, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,12 @@ on boot
|
||||
chmod 0664 /dev/cpuset/moderate/tasks
|
||||
chmod 0664 /dev/cpuset/moderate/cpus
|
||||
|
||||
chown system graphics /sys/class/drm/sde-crtc-0/measured_fps
|
||||
chmod 0660 /sys/class/drm/sde-crtc-0/measured_fps
|
||||
|
||||
chown system system /sys/class/thermal/thermal_zone78/temp
|
||||
chmod 0660 /sys/class/thermal/thermal_zone78/temp
|
||||
|
||||
# SWAP
|
||||
on boot
|
||||
write /proc/sys/vm/swappiness 130
|
||||
|
||||
Reference in New Issue
Block a user