Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d298328f6d | ||
|
|
d766cad887 | ||
|
|
10164c7973 | ||
|
|
c4f6acb349 | ||
|
|
6012fb4169 | ||
|
|
50dbb8e392 | ||
|
|
a571d2c121 | ||
|
|
938a92001e | ||
|
|
e02936ba22 | ||
|
|
262e92a301 | ||
|
|
287edef643 | ||
|
|
e0b53bfceb | ||
|
|
cfd4a67773 | ||
|
|
002221bc2d | ||
|
|
bd68fd2f5c | ||
|
|
76d32ae34b | ||
|
|
6e67b7d1d2 | ||
|
|
1338bf7a9f | ||
|
|
42ee6eb842 | ||
|
|
0e220741ba | ||
|
|
c75adc316a | ||
|
|
3241e623c7 | ||
|
|
19bd3280b1 | ||
|
|
4be9ebc30a | ||
|
|
10861470f3 | ||
|
|
74fcd1e347 | ||
|
|
f009924a41 | ||
|
|
cb944af083 | ||
|
|
9d5d4d111b | ||
|
|
ecff11a1d3 | ||
|
|
d1f4474d48 | ||
|
|
7ae39dc780 | ||
|
|
a1c336578f | ||
|
|
1d703b5ffa | ||
|
|
97aac6da03 | ||
|
|
8bae3deb28 | ||
|
|
e1bca51105 | ||
|
|
951ae51350 | ||
|
|
747995f07d | ||
|
|
17662c88cc | ||
|
|
3e2560075a | ||
|
|
94f75e5f22 | ||
|
|
e78b5be3f2 | ||
|
|
fbd5f1a416 | ||
|
|
601baf8ab8 | ||
|
|
7e9d524ad6 | ||
|
|
c8176ec118 | ||
|
|
f71da59c08 | ||
|
|
4501bce205 | ||
|
|
1e1434cd33 | ||
|
|
eb62287e5e | ||
|
|
12b640f71a | ||
|
|
5c65a3d6bc | ||
|
|
136de7c630 | ||
|
|
f83c0560d9 | ||
|
|
7b8f8e55e5 | ||
|
|
5dae09ef5e | ||
|
|
419b851b90 | ||
|
|
50f647e811 | ||
|
|
993493dc3c |
@@ -2,6 +2,7 @@ soong_namespace {
|
||||
imports: [
|
||||
"hardware/google/interfaces",
|
||||
"hardware/google/pixel",
|
||||
"bootable/deprecated-ota",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -32,17 +32,17 @@ BUILD_BROKEN_INCORRECT_PARTITION_IMAGES := true
|
||||
|
||||
# Architecture
|
||||
TARGET_ARCH := arm64
|
||||
TARGET_ARCH_VARIANT := armv8-a
|
||||
TARGET_ARCH_VARIANT := armv8-2a-dotprod
|
||||
TARGET_CPU_ABI := arm64-v8a
|
||||
TARGET_CPU_ABI2 :=
|
||||
TARGET_CPU_VARIANT := generic
|
||||
TARGET_CPU_VARIANT := cortex-a76
|
||||
TARGET_CPU_VARIANT_RUNTIME := cortex-a76
|
||||
|
||||
TARGET_2ND_ARCH := arm
|
||||
TARGET_2ND_ARCH_VARIANT := armv8-a
|
||||
TARGET_2ND_ARCH_VARIANT := armv8-2a
|
||||
TARGET_2ND_CPU_ABI := armeabi-v7a
|
||||
TARGET_2ND_CPU_ABI2 := armeabi
|
||||
TARGET_2ND_CPU_VARIANT := generic
|
||||
TARGET_2ND_CPU_VARIANT := cortex-a76
|
||||
TARGET_2ND_CPU_VARIANT_RUNTIME := cortex-a76
|
||||
|
||||
# Audio
|
||||
|
||||
6
audio/vendor/audio_effects.xml
vendored
6
audio/vendor/audio_effects.xml
vendored
@@ -52,6 +52,9 @@
|
||||
<!--DOLBY VQE-->
|
||||
<library name="vqe" path="libswvqe.so"/>
|
||||
<!--DOLBY END-->
|
||||
<!--Spatial Audio-->
|
||||
<library name="spatializer" path="libspatialaudio.so"/>
|
||||
<!--Spatial Audio-->
|
||||
</libraries>
|
||||
<effects>
|
||||
<effectProxy name="bassboost" library="proxy" uuid="14804144-a5ee-4d24-aa88-0002a5d5c51b">
|
||||
@@ -116,6 +119,9 @@
|
||||
<!--DOLBY VQE-->
|
||||
<effect name="vqe" library="vqe" uuid="64a0f614-7fa4-48b8-b081-d59dc954616f"/>
|
||||
<!--DOLBY END-->
|
||||
<!--Spatial Audio-->
|
||||
<effect name="spatializer" library="spatializer" uuid="cc4677de-ff72-11eb-9a03-0242ac130003"/>
|
||||
<!--Spatial Audio-->
|
||||
</effects>
|
||||
<postprocess>
|
||||
<stream type="music">
|
||||
|
||||
22
audio/vendor/audio_policy_configuration.xml
vendored
22
audio/vendor/audio_policy_configuration.xml
vendored
@@ -81,6 +81,12 @@
|
||||
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</mixPort>
|
||||
<mixPort name="hifi_playback" role="source" />
|
||||
<!--Spatial Audio-->
|
||||
<mixPort name="immersive_out" role="source" flags="AUDIO_OUTPUT_FLAG_SPATIALIZER">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
|
||||
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
<!--Spatial Audio-->
|
||||
</mixPort>
|
||||
<mixPort name="compress_passthrough" role="source"
|
||||
flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING">
|
||||
<profile name="" format="dynamic"
|
||||
@@ -322,11 +328,11 @@
|
||||
<route type="mix" sink="Earpiece"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out"/>
|
||||
<route type="mix" sink="Speaker"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out,immersive_out"/>
|
||||
<route type="mix" sink="Wired Headset"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,dsd_compress_passthrough,voip_rx,mmap_no_irq_out"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,dsd_compress_passthrough,voip_rx,mmap_no_irq_out,immersive_out"/>
|
||||
<route type="mix" sink="Wired Headphones"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,dsd_compress_passthrough,voip_rx,mmap_no_irq_out"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,dsd_compress_passthrough,voip_rx,mmap_no_irq_out,immersive_out"/>
|
||||
<route type="mix" sink="Line"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,dsd_compress_passthrough,voip_rx,mmap_no_irq_out"/>
|
||||
<route type="mix" sink="HDMI"
|
||||
@@ -336,15 +342,15 @@
|
||||
<route type="mix" sink="FM"
|
||||
sources="primary output"/>
|
||||
<route type="mix" sink="BT SCO"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,immersive_out"/>
|
||||
<route type="mix" sink="BT SCO Headset"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,immersive_out"/>
|
||||
<route type="mix" sink="BT SCO Car Kit"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,immersive_out"/>
|
||||
<route type="mix" sink="USB Device Out"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out,hifi_playback"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out,hifi_playback,immersive_out"/>
|
||||
<route type="mix" sink="USB Headset Out"
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out,hifi_playback"/>
|
||||
sources="primary output,raw,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out,hifi_playback,immersive_out"/>
|
||||
#else VENDOR_EDIT -->
|
||||
<route type="mix" sink="Earpiece"
|
||||
sources="primary output,deep_buffer,direct_pcm,compressed_offload,voip_rx,mmap_no_irq_out"/>
|
||||
|
||||
55
common.mk
55
common.mk
@@ -66,12 +66,6 @@ PRODUCT_COPY_FILES += \
|
||||
$(call find-copy-subdir-files,*,$(LOCAL_PATH)/audio/odm/,$(TARGET_COPY_OUT_ODM)/etc) \
|
||||
$(call find-copy-subdir-files,*,$(LOCAL_PATH)/audio/vendor/,$(TARGET_COPY_OUT_VENDOR)/etc)
|
||||
|
||||
# AVB
|
||||
#PRODUCT_PACKAGES += \
|
||||
# q-gsi.avbpubkey \
|
||||
# r-gsi.avbpubkey \
|
||||
# s-gsi.avbpubkey
|
||||
|
||||
# Bluetooth
|
||||
PRODUCT_PACKAGES += \
|
||||
audio.bluetooth.default
|
||||
@@ -90,10 +84,6 @@ PRODUCT_COPY_FILES += \
|
||||
TARGET_SCREEN_HEIGHT := 2400
|
||||
TARGET_SCREEN_WIDTH := 1080
|
||||
|
||||
# Call recording
|
||||
#PRODUCT_PACKAGES += \
|
||||
# com.google.android.apps.dialer.call_recording_audio.features.xml
|
||||
|
||||
# Camera
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.camera.provider@2.4-impl \
|
||||
@@ -109,9 +99,18 @@ PRODUCT_COPY_FILES += \
|
||||
#CertifiedProps
|
||||
$(call inherit-product-if-exists, vendor/certprops/Android.mk)
|
||||
|
||||
# Display
|
||||
# Core Packages
|
||||
PRODUCT_PACKAGES += \
|
||||
Parts
|
||||
|
||||
# Config Store
|
||||
PRODUCT_PACKAGES += \
|
||||
disable_configstore \
|
||||
vendor.qti.hardware.capabilityconfigstore@1.0 \
|
||||
vendor.qti.hardware.capabilityconfigstore@1.0.vendor
|
||||
|
||||
# Display
|
||||
PRODUCT_PACKAGES += \
|
||||
gralloc.qcom \
|
||||
hwcomposer.qcom \
|
||||
libdisplayconfig.qti \
|
||||
@@ -189,8 +188,8 @@ PRODUCT_COPY_FILES += \
|
||||
|
||||
# Health
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.health@2.1-impl-qti \
|
||||
android.hardware.health@2.1-service
|
||||
android.hardware.health-service.qti \
|
||||
android.hardware.health-service.qti_recovery
|
||||
|
||||
# HIDL
|
||||
PRODUCT_PACKAGES += \
|
||||
@@ -271,9 +270,11 @@ PRODUCT_PACKAGES += \
|
||||
#Lawnchair
|
||||
$(call inherit-product-if-exists, vendor/lawnchair/lawnchair.mk)
|
||||
|
||||
# LiveDisplay
|
||||
# PRODUCT_PACKAGES += \
|
||||
# vendor.lineage.livedisplay@2.0-service-sdm
|
||||
#LiveDisplay
|
||||
PRODUCT_PACKAGES += \
|
||||
vendor.lineage.livedisplay-service.sdm
|
||||
|
||||
$(call soong_config_set,livedisplay_sdm,enable_dm,false)
|
||||
|
||||
# Media
|
||||
PRODUCT_PACKAGES += \
|
||||
@@ -329,7 +330,8 @@ PRODUCT_PACKAGES += \
|
||||
# Overlays
|
||||
DEVICE_PACKAGE_OVERLAYS += \
|
||||
$(LOCAL_PATH)/overlay \
|
||||
$(LOCAL_PATH)/overlay-lineage
|
||||
$(LOCAL_PATH)/overlay-lineage \
|
||||
$(LOCAL_PATH)/overlay-evolution
|
||||
|
||||
# Perf
|
||||
PRODUCT_PACKAGES += \
|
||||
@@ -345,7 +347,6 @@ PRODUCT_PROPERTY_OVERRIDES += \
|
||||
# Power
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.power-service.lineage-libperfmgr \
|
||||
android.hardware.power@1.2.vendor \
|
||||
libqti-perfd-client
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
@@ -383,8 +384,8 @@ PRODUCT_PACKAGES += \
|
||||
|
||||
# Sensors
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.sensors@2.0-service.multihal_realme_sm7125
|
||||
|
||||
android.hardware.sensors-service.multihal
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
frameworks/native/data/etc/android.hardware.sensor.accelerometer.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.accelerometer.xml \
|
||||
frameworks/native/data/etc/android.hardware.sensor.compass.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.sensor.compass.xml \
|
||||
@@ -402,12 +403,16 @@ PRODUCT_PACKAGES += \
|
||||
vendor.qti.hardware.servicetracker@1.1.vendor \
|
||||
vendor.qti.hardware.servicetracker@1.2.vendor
|
||||
|
||||
# Disable Scudo to save RAM.
|
||||
PRODUCT_DISABLE_SCUDO := true
|
||||
|
||||
# Soong namespaces
|
||||
PRODUCT_SOONG_NAMESPACES += \
|
||||
$(LOCAL_PATH) \
|
||||
kernel/realme/sm7125 \
|
||||
hardware/google/interfaces \
|
||||
hardware/google/pixel \
|
||||
bootable/deprecated-ota \
|
||||
hardware/lineage/interfaces/power-libperfmgr \
|
||||
hardware/oplus \
|
||||
hardware/qcom-caf/common/libqti-perfd-client
|
||||
@@ -454,10 +459,6 @@ PRODUCT_PACKAGES += \
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.thermal-service.qti
|
||||
|
||||
# TextClassifier
|
||||
#PRODUCT_PACKAGES += \
|
||||
# textclassifier.bundle1
|
||||
|
||||
# Touchscreen
|
||||
PRODUCT_COPY_FILES += \
|
||||
frameworks/native/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml
|
||||
@@ -477,6 +478,9 @@ PRODUCT_COPY_FILES += \
|
||||
PRODUCT_PACKAGES += \
|
||||
vendor.qti.hardware.vibrator.service
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
vendor/qcom/opensource/vibrator/excluded-input-devices.xml:$(TARGET_COPY_OUT_VENDOR)/etc/excluded-input-devices.xml
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
prebuilts/vndk/v30/arm64/arch-arm64-armv8-a/shared/vndk-core/libgui.so:$(TARGET_COPY_OUT_SYSTEM)/lib64/libxxx.so \
|
||||
prebuilts/vndk/v30/arm64/arch-arm-armv8-a/shared/vndk-core/libgui.so:$(TARGET_COPY_OUT_SYSTEM)/lib/libxxx.so \
|
||||
@@ -525,9 +529,6 @@ PRODUCT_PACKAGES += \
|
||||
firmware_wlan_mac.bin_symlink \
|
||||
firmware_WCNSS_qcom_cfg.ini_symlink
|
||||
|
||||
#PRODUCT_BOOT_JARS += \
|
||||
WfdCommon
|
||||
|
||||
# Build Specific VNDK 30 Libs for Android 12+
|
||||
PRODUCT_PACKAGES += \
|
||||
android.frameworks.automotive.display@1.0.vendor \
|
||||
|
||||
2
configs/media/vendor/media_codecs_c2.xml
vendored
2
configs/media/vendor/media_codecs_c2.xml
vendored
@@ -64,7 +64,7 @@
|
||||
<Limit name="size" min="96x96" max="4096x2160" />
|
||||
<Limit name="alignment" value="2x2" />
|
||||
<Limit name="block-size" value="16x16" />
|
||||
<Limit name="blocks-per-second" range="24-979200" />
|
||||
<Limit name="blocks-per-second" range="36-979200" />
|
||||
<Limit name="bitrate" range="1-100000000" />
|
||||
<Limit name="frame-rate" range="1-240" />
|
||||
<Limit name="concurrent-instances" max="16" />
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[
|
||||
{
|
||||
"repository": "crdroidandroid/android_hardware_dolby",
|
||||
"target_path": "hardware/dolby",
|
||||
"branch": "15.0"
|
||||
}
|
||||
]
|
||||
@@ -2,7 +2,10 @@ service vendor.fps_hal.realme_sm7125 /vendor/bin/hw/android.hardware.biometrics.
|
||||
# "class hal" causes a race condition on some devices due to files created
|
||||
# in /data. As a workaround, postpone startup until later in boot once
|
||||
# /data is mounted.
|
||||
class late_start
|
||||
class hal
|
||||
user system
|
||||
group system input uhid
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
|
||||
on post-fs-data
|
||||
start vendor.fps_hal.realme_sm7125
|
||||
|
||||
@@ -659,15 +659,6 @@
|
||||
<instance>oplus_slot2</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<!-- <hal format="hidl">
|
||||
<name>vendor.lineage.livedisplay</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>2.0</version>
|
||||
<interface>
|
||||
<name>IPictureAdjustment</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal> -->
|
||||
<hal format="hidl">
|
||||
<name>vendor.qti.hardware.data.iwlan</name>
|
||||
<transport>hwbinder</transport>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019-2025 The Evolution X Project
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<!-- Android version screen, build maintainer -->
|
||||
<string name="build_maintainer_summary" translatable="false">SHAHID_SHAMIM</string>
|
||||
<string name="build_maintainer_donate_url" translatable="false">https://paypal.me/shaenix</string>
|
||||
</resources>
|
||||
@@ -550,9 +550,6 @@
|
||||
<!-- Whether to show min/max refresh rate in display settings -->
|
||||
<bool name="config_show_refresh_rate_controls">true</bool>
|
||||
|
||||
<!-- Indicates whether device has a power button fingerprint sensor. -->
|
||||
<bool name="config_is_powerbutton_fps" translatable="false" >true</bool>
|
||||
|
||||
<!-- Control the behavior when the user long presses the power button.
|
||||
0 - Nothing
|
||||
1 - Global actions menu
|
||||
@@ -592,4 +589,21 @@
|
||||
|
||||
<!-- Whether we use large screen shade header which takes only one row compared to QS header -->
|
||||
<bool name="config_use_large_screen_shade_header">true</bool>
|
||||
</resources>
|
||||
|
||||
<!-- Boost Framework -->
|
||||
<bool name="config_supportsBoostFramework">true</bool>
|
||||
|
||||
<!-- Whether WiFi display is supported by this device.
|
||||
There are many prerequisites for this feature to work correctly.
|
||||
Here are a few of them:
|
||||
* The WiFi radio must support WiFi P2P.
|
||||
* The WiFi radio must support concurrent connections to the WiFi display and
|
||||
to an access point.
|
||||
* The Audio Server audio_policy_configuration.xml file must specify a rule for
|
||||
the "r_submix" remote submix module. This module is used to record and stream system
|
||||
audio output to the WiFi display encoder in the media server.
|
||||
* The remote submix module "audio.r_submix.default" must be installed on the device.
|
||||
* The device must be provisioned with HDCP keys (for protected content).
|
||||
-->
|
||||
<bool name="config_enableWifiDisplay">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -15,10 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Height of the status bar. Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead.-->
|
||||
|
||||
<dimen name="status_bar_height">100px</dimen>
|
||||
|
||||
<!-- Height of the status bar in portrait -->
|
||||
<dimen name="status_bar_height_portrait">110px</dimen>
|
||||
<dimen name="status_bar_height_portrait">100px</dimen>
|
||||
|
||||
<!-- Height of the status bar in landscape -->
|
||||
<dimen name="status_bar_height_landscape">82px</dimen>
|
||||
|
||||
@@ -39,5 +39,7 @@
|
||||
|
||||
<!-- Enable vibrate for calls by default -->
|
||||
<bool name="def_vibrate_when_ringing">true</bool>
|
||||
|
||||
|
||||
<!-- Speed up the animations -->
|
||||
<fraction name="def_window_transition_scale">50%</fraction>
|
||||
</resources>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<dimen name="status_bar_padding_end">16px</dimen>
|
||||
|
||||
<!-- the padding on the top of the statusbar -->
|
||||
<dimen name="status_bar_padding_top">8dp</dimen>
|
||||
<dimen name="status_bar_padding_top">4dp</dimen>
|
||||
|
||||
<!-- Starting padding for a left-aligned status bar clock -->
|
||||
<dimen name="status_bar_left_clock_starting_padding">6px</dimen>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Enable auxiliary cameras selector. -->
|
||||
<bool name="config_enableAuxCameras">true</bool>
|
||||
|
||||
<!-- List of ID of auxiliary cameras that must be ignored by the app. -->
|
||||
|
||||
<string-array name="config_ignoredAuxCameraIds">
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
</string-array>
|
||||
|
||||
<!-- List of ID of camera devices that doesn't advertise
|
||||
REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE yet are
|
||||
able to produce a JPEG or a YUV stream. The camera IDs specified
|
||||
here won't be excluded by CameraX. Please be sure to test all
|
||||
modes exposed by the app with those cameras. -->
|
||||
<!-- <string-array name="config_backwardCompatibleCameraIds">
|
||||
<item>30</item>
|
||||
</string-array> -->
|
||||
|
||||
<!-- New flag to allow aperture in aux package allowlist -->
|
||||
<bool name="config_cameraAuxPackageAllowlist">true</bool>
|
||||
</resources>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,14 +24,6 @@
|
||||
devices will be able to vary their amplitude but do not possess enough dynamic range to
|
||||
have distinct intensity levels -->
|
||||
<bool name="config_vibration_supports_multiple_intensities">true</bool>
|
||||
|
||||
<!-- Defines the location of the fingerprint sensor on the device
|
||||
0 = back
|
||||
1 = front
|
||||
2 = left side
|
||||
3 = right side
|
||||
-->
|
||||
<integer name="config_fingerprintSensorLocation">3</integer>
|
||||
|
||||
<!-- Whether this device is supporting the microphone toggle -->
|
||||
<bool name="config_supportsMicToggle">true</bool>
|
||||
|
||||
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,
|
||||
}
|
||||
210
parts/AndroidManifest.xml
Normal file
210
parts/AndroidManifest.xml
Normal file
@@ -0,0 +1,210 @@
|
||||
<?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-permission android:name="android.permission.BATTERY_STATS" />
|
||||
<uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Charging Bypass Activity -->
|
||||
<activity
|
||||
android:name=".charge.ChargeActivity"
|
||||
android:label="@string/charge_bypass_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.battery" />
|
||||
<meta-data
|
||||
android:name="com.android.settings.summary"
|
||||
android:resource="@string/charge_bypass_summary" />
|
||||
</activity>
|
||||
|
||||
<!-- Charging Bypass Monitoring Service -->
|
||||
<service
|
||||
android:name=".charge.ChargeBatteryMonitorService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="specialUse">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="battery_optimization" />
|
||||
</service>
|
||||
|
||||
<!-- Charging Bypass Boot Receiver -->
|
||||
<receiver
|
||||
android:name=".charge.ChargeReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Charging Bypass Quick Settings Tile -->
|
||||
<service
|
||||
android:name=".charge.ChargeQSTile"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/ic_charge_bypass"
|
||||
android:label="@string/charge_bypass_qs_tile_label"
|
||||
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.ACTIVE_TILE"
|
||||
android:value="false" />
|
||||
</service>
|
||||
|
||||
<!-- Charging information tile service -->
|
||||
<service
|
||||
android:name=".charginginfo.ChargingInfoTileService"
|
||||
android:icon="@drawable/ic_charging_speed"
|
||||
android:label="@string/charging_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="false" />
|
||||
</service>
|
||||
|
||||
<!-- GameBar Overlay -->
|
||||
<activity
|
||||
android:name=".gamebar.GameBarSettingsActivity"
|
||||
android:label="@string/game_bar_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_bar_summary" />
|
||||
</activity>
|
||||
|
||||
<!-- GameBar AppSelector -->
|
||||
<activity android:name=".gamebar.GameBarAppSelectorActivity" />
|
||||
<activity android:name=".gamebar.GameBarAppRemoverActivity" />
|
||||
|
||||
<!-- GameBar Overlay Tile Service -->
|
||||
<service
|
||||
android:name=".gamebar.GameBarTileService"
|
||||
android:label="@string/game_bar_tile_label"
|
||||
android:icon="@drawable/ic_gamebar"
|
||||
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 Overlay Monitor Service -->
|
||||
<service
|
||||
android:name=".gamebar.GameBarMonitorService"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- GameBar BootReceiver -->
|
||||
<receiver
|
||||
android:name=".gamebar.GameBarBootReceiver"
|
||||
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>
|
||||
@@ -1,24 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The LineageOS Project
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<!-- Defines the location of the fingerprint sensor on the device
|
||||
0 = back
|
||||
1 = front
|
||||
2 = left side
|
||||
3 = right side
|
||||
-->
|
||||
<integer name="config_fingerprintSensorLocation">3</integer>
|
||||
</resources>
|
||||
<permissions>
|
||||
<privapp-permissions package="org.lineageos.settings">
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
0
parts/proguard.flags
Normal file
0
parts/proguard.flags
Normal file
13
parts/res/drawable/ic_battery_discharge.xml
Normal file
13
parts/res/drawable/ic_battery_discharge.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<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/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM15,20H9V6h6V20z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,8h6v2H9z"/>
|
||||
</vector>
|
||||
16
parts/res/drawable/ic_battery_unknown.xml
Normal file
16
parts/res/drawable/ic_battery_unknown.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<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/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM15,20H9V6h6V20z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M13,13a1,1 0 1,1 -2,0a1,1 0 1,1 2,0"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,10.5c-0.83,0 -1.5,0.67 -1.5,1.5h1c0,-0.28 0.22,-0.5 0.5,-0.5s0.5,0.22 0.5,0.5c0,0.5 -0.75,0.75 -0.75,1.5h1c0,-0.38 0.75,-0.75 0.75,-1.5C13.5,11.17 12.83,10.5 12,10.5z"/>
|
||||
</vector>
|
||||
26
parts/res/drawable/ic_charge_bypass.xml
Normal file
26
parts/res/drawable/ic_charge_bypass.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorOnSurface">
|
||||
|
||||
<!-- Battery outline -->
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.67,4H14V2c0,-0.55 -0.45,-1 -1,-1h-2c-0.55,0 -1,0.45 -1,1v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
|
||||
|
||||
<!-- Bypass indicator (diagonal line) -->
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M6,6l12,12"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round"/>
|
||||
|
||||
<!-- Lightning bolt (partially visible) -->
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M11,9l1.5,0l-1.5,3l1,0l-1.5,3l-1.5,0l1.5,-3l-1,0z"/>
|
||||
|
||||
</vector>
|
||||
13
parts/res/drawable/ic_charging_speed.xml
Normal file
13
parts/res/drawable/ic_charging_speed.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<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/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M11,20v-5.5H9L13,7v5.5h2L11,20z"/>
|
||||
</vector>
|
||||
10
parts/res/drawable/ic_error.xml
Normal file
10
parts/res/drawable/ic_error.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<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/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||
</vector>
|
||||
11
parts/res/drawable/ic_gamebar.xml
Normal file
11
parts/res/drawable/ic_gamebar.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="24.0dp"
|
||||
android:height="24.0dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7,6H17A6,6 0 0,1 23,12A6,6 0 0,1 17,18C15.22,18 13.63,17.23 12.53,16H11.47C10.37,17.23 8.78,18 7,18A6,6 0 0,1 1,12A6,6 0 0,1 7,6M6,9V11H4V13H6V15H8V13H10V11H8V9H6M15.5,12A1.5,1.5 0 0,0 14,13.5A1.5,1.5 0 0,0 15.5,15A1.5,1.5 0 0,0 17,13.5A1.5,1.5 0 0,0 15.5,12M18.5,9A1.5,1.5 0 0,0 17,10.5A1.5,1.5 0 0,0 18.5,12A1.5,1.5 0 0,0 20,10.5A1.5,1.5 0 0,0 18.5,9Z" />
|
||||
</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_bar.xml
Normal file
12
parts/res/layout/activity_game_bar.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_bar_fragment"
|
||||
android:name="org.lineageos.settings.gamebar.GameBarFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
5
parts/res/layout/activity_game_bar_app_selector.xml
Normal file
5
parts/res/layout/activity_game_bar_app_selector.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/content_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
10
parts/res/layout/game_bar.xml
Normal file
10
parts/res/layout/game_bar.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_bar_root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#80000000"
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
6
parts/res/layout/game_bar_app_selector.xml
Normal file
6
parts/res/layout/game_bar_app_selector.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/app_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
34
parts/res/layout/game_bar_app_selector_item.xml
Normal file
34
parts/res/layout/game_bar_app_selector_item.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/app_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_package"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
108
parts/res/values/arrays.xml
Normal file
108
parts/res/values/arrays.xml
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2025 The Android Open Source Project
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources>
|
||||
<!-- FPS Overlay -->
|
||||
<string-array name="game_bar_fps_method_entries">
|
||||
<item>New API (Default)</item>
|
||||
<item>Legacy Sysfs</item>
|
||||
</string-array>
|
||||
<string-array name="game_bar_fps_method_values">
|
||||
<item>new</item>
|
||||
<item>legacy</item>
|
||||
</string-array>
|
||||
|
||||
<!-- 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_bar_format_entries">
|
||||
<item>Full</item>
|
||||
<item>Minimal</item>
|
||||
</string-array>
|
||||
<string-array name="game_bar_format_values">
|
||||
<item>full</item>
|
||||
<item>minimal</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Split Mode -->
|
||||
<string-array name="game_bar_split_mode_entries">
|
||||
<item>Side-by-Side</item>
|
||||
<item>Stacked</item>
|
||||
</string-array>
|
||||
<string-array name="game_bar_split_mode_values">
|
||||
<item>side_by_side</item>
|
||||
<item>stacked</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Long press timeouts -->
|
||||
<string-array name="game_bar_longpress_entries">
|
||||
<item>1 second</item>
|
||||
<item>3 seconds</item>
|
||||
<item>5 seconds</item>
|
||||
<item>10 seconds</item>
|
||||
</string-array>
|
||||
<string-array name="game_bar_longpress_values">
|
||||
<item>1000</item>
|
||||
<item>3000</item>
|
||||
<item>5000</item>
|
||||
<item>10000</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
7
parts/res/values/attrs.xml
Normal file
7
parts/res/values/attrs.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="MinMaxSeekBarPreference">
|
||||
<attr name="minValue" format="integer" />
|
||||
<attr name="maxValue" format="integer" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
90
parts/res/values/strings.xml
Normal file
90
parts/res/values/strings.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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>
|
||||
|
||||
<!-- GameBar Overlay -->
|
||||
<string name="game_bar_title">GameBar</string>
|
||||
<string name="game_bar_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_bar_tile_label">GameBar</string>
|
||||
<string name="game_bar_tile_description">Toggle the game overlay</string>
|
||||
|
||||
<!-- Charging information tile -->
|
||||
<string name="charging_tile_label">Charging Speed</string>
|
||||
<string name="charging_tile_information">Shows real-time charging/discharging power</string>
|
||||
|
||||
<!-- Tile states -->
|
||||
<string name="charging_tile_charging">Charging</string>
|
||||
<string name="charging_tile_discharging">Discharging</string>
|
||||
<string name="charging_tile_not_charging">Not Charging</string>
|
||||
<string name="charging_tile_error">Error</string>
|
||||
|
||||
<!-- Content descriptions for accessibility -->
|
||||
<string name="charging_tile_desc_charging">Charging at %1$s watts, %2$s amps, %3$s volts</string>
|
||||
<string name="charging_tile_desc_discharging">Discharging at %1$s watts</string>
|
||||
<string name="charging_tile_desc_not_charging">Battery not charging or discharging</string>
|
||||
<string name="charging_tile_desc_error">Unable to read charging information</string>
|
||||
|
||||
<!-- Charge bypass strings -->
|
||||
<string name="charge_bypass_title">Charging Bypass</string>
|
||||
<string name="charge_bypass_summary">Configure charging bypass settings</string>
|
||||
<string name="charge_bypass_warning">Enabling charging bypass will prevent the battery from charging while plugged in. This may affect battery longevity if used incorrectly.</string>
|
||||
<string name="charge_bypass_unavailable">Charging bypass is not supported on this device</string>
|
||||
|
||||
<!-- Master toggle strings -->
|
||||
<string name="charge_bypass_master_title">Enable Charging Bypass</string>
|
||||
<string name="charge_bypass_master_summary">Allow charging bypass based on selected configuration</string>
|
||||
|
||||
<!-- Configuration category -->
|
||||
<string name="charge_bypass_configuration_title">Bypass Configuration</string>
|
||||
|
||||
<!-- Manual mode strings -->
|
||||
<string name="charge_bypass_manual_title">Manual Control</string>
|
||||
<string name="charge_bypass_manual_summary">Control charging bypass through Quick Settings tile</string>
|
||||
|
||||
<!-- Smart mode strings -->
|
||||
<string name="charge_bypass_smart_title">Smart Control</string>
|
||||
<string name="charge_bypass_smart_summary">Automatically manage charging bypass based on battery level</string>
|
||||
|
||||
<!-- Battery target strings -->
|
||||
<string name="charge_bypass_battery_target_title">Battery Target</string>
|
||||
<string name="charge_bypass_battery_target_summary">Bypass charging when battery reaches this level (%d%%)</string>
|
||||
|
||||
<!-- QS Tile strings -->
|
||||
<string name="charge_bypass_qs_tile_label">Charging Bypass</string>
|
||||
<string name="charge_bypass_qs_active">Bypass Active</string>
|
||||
<string name="charge_bypass_qs_inactive">Bypass Inactive</string>
|
||||
<string name="charge_bypass_qs_disabled">Bypass Disabled</string>
|
||||
|
||||
<!-- Notification strings -->
|
||||
<string name="charge_bypass_notification_title">Charging Bypass Active</string>
|
||||
<string name="charge_bypass_notification_text">Battery charging is currently bypassed</string>
|
||||
<string name="charge_bypass_smart_enabled">Smart charging bypass enabled at %d%%</string>
|
||||
<string name="charge_bypass_smart_disabled">Smart charging bypass disabled</string>
|
||||
|
||||
</resources>
|
||||
42
parts/res/xml/charge_settings.xml
Normal file
42
parts/res/xml/charge_settings.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Main bypass toggle -->
|
||||
<SwitchPreference
|
||||
android:key="bypass_charge_master"
|
||||
android:title="@string/charge_bypass_master_title"
|
||||
android:summary="@string/charge_bypass_master_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<!-- Configuration category -->
|
||||
<PreferenceCategory
|
||||
android:key="bypass_configuration_category"
|
||||
android:title="@string/charge_bypass_configuration_title">
|
||||
|
||||
<!-- Manual bypass option -->
|
||||
<CheckBoxPreference
|
||||
android:key="bypass_mode_manual"
|
||||
android:title="@string/charge_bypass_manual_title"
|
||||
android:summary="@string/charge_bypass_manual_summary"
|
||||
android:defaultValue="true" />
|
||||
|
||||
<!-- Smart bypass option -->
|
||||
<CheckBoxPreference
|
||||
android:key="bypass_mode_smart"
|
||||
android:title="@string/charge_bypass_smart_title"
|
||||
android:summary="@string/charge_bypass_smart_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<!-- Battery target slider (only visible in smart mode) -->
|
||||
<org.lineageos.settings.charge.MinMaxSeekBarPreference
|
||||
android:key="battery_target_slider"
|
||||
android:title="@string/charge_bypass_battery_target_title"
|
||||
android:summary="@string/charge_bypass_battery_target_summary"
|
||||
android:min="50"
|
||||
android:max="100"
|
||||
android:defaultValue="80"
|
||||
android:dependency="bypass_mode_smart" />
|
||||
|
||||
</PreferenceScreen>
|
||||
252
parts/res/xml/game_bar_preferences.xml
Normal file
252
parts/res/xml/game_bar_preferences.xml
Normal file
@@ -0,0 +1,252 @@
|
||||
<?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">
|
||||
|
||||
<com.android.settingslib.widget.MainSwitchPreference
|
||||
android:key="game_bar_enable"
|
||||
android:title="Enable GameBar Overlay"
|
||||
android:summary="@string/game_bar_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Overlay Features"
|
||||
android:dependency="game_bar_enable">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_fps_enable"
|
||||
android:title="FPS Overlay"
|
||||
android:summary="Show current FPS on screen"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_temp_enable"
|
||||
android:title="Device Temperature"
|
||||
android:summary="Show device (battery) temperature"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_cpu_usage_enable"
|
||||
android:title="CPU Usage"
|
||||
android:summary="Show current CPU usage percentage"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_cpu_clock_enable"
|
||||
android:title="CPU Clock Speeds"
|
||||
android:summary="Show current CPU clock speeds for each core"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_cpu_temp_enable"
|
||||
android:title="CPU Temperature"
|
||||
android:summary="Show CPU temperature (thermal_zone0)"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_ram_enable"
|
||||
android:title="RAM Usage"
|
||||
android:summary="Show current RAM usage in MB"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_gpu_usage_enable"
|
||||
android:title="GPU Usage"
|
||||
android:summary="Show GPU usage percentage"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_gpu_clock_enable"
|
||||
android:title="GPU Clock Speed"
|
||||
android:summary="Show current GPU clock frequency"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_gpu_temp_enable"
|
||||
android:title="GPU Temperature"
|
||||
android:summary="Show current GPU temperature"
|
||||
android:defaultValue="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="FPS Measurement Method"
|
||||
android:dependency="game_bar_fps_enable">
|
||||
<ListPreference
|
||||
android:key="game_bar_fps_method"
|
||||
android:title="Select FPS Method"
|
||||
android:summary="Choose between the New API method (default) and Legacy Sysfs"
|
||||
android:defaultValue="new"
|
||||
android:entries="@array/game_bar_fps_method_entries"
|
||||
android:entryValues="@array/game_bar_fps_method_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Customization"
|
||||
android:dependency="game_bar_enable">
|
||||
|
||||
<SeekBarPreference
|
||||
android:key="game_bar_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_bar_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_bar_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_bar_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_bar_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_bar_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_bar_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_bar_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_bar_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_bar_format"
|
||||
android:title="Overlay Format"
|
||||
android:summary="Choose between Full or Minimal display"
|
||||
android:defaultValue="full"
|
||||
android:entries="@array/game_bar_format_entries"
|
||||
android:entryValues="@array/game_bar_format_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Split Config"
|
||||
android:dependency="game_bar_enable">
|
||||
|
||||
<ListPreference
|
||||
android:key="game_bar_split_mode"
|
||||
android:title="Split Mode"
|
||||
android:summary="Choose Side-by-Side or Stacked arrangement"
|
||||
android:defaultValue="stacked"
|
||||
android:entries="@array/game_bar_split_mode_entries"
|
||||
android:entryValues="@array/game_bar_split_mode_values" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Overlay Gesture Controls"
|
||||
android:dependency="game_bar_enable">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_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" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_doubletap_capture"
|
||||
android:title="Enable Double Tap to Capture"
|
||||
android:summary="Double-tap overlay to start/stop capture logs"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_longpress_enable"
|
||||
android:title="Enable Long Press"
|
||||
android:summary="Long-press overlay to access GameBar settings"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<ListPreference
|
||||
android:key="game_bar_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_bar_longpress_entries"
|
||||
android:entryValues="@array/game_bar_longpress_values"
|
||||
android:dependency="game_bar_longpress_enable" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Capture Logs"
|
||||
android:dependency="game_bar_enable">
|
||||
|
||||
<Preference
|
||||
android:key="game_bar_capture_start"
|
||||
android:title="Start Logging"
|
||||
android:summary="Begin capturing FPS and performance data in real-time" />
|
||||
|
||||
<Preference
|
||||
android:key="game_bar_capture_stop"
|
||||
android:title="Stop Logging"
|
||||
android:summary="Stop capturing FPS and performance data in real-time" />
|
||||
|
||||
<Preference
|
||||
android:key="game_bar_capture_export"
|
||||
android:title="Export GameBar Log Data"
|
||||
android:summary="Save the captured FPS and performance data as a CSV file" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Auto Enable GameBar">
|
||||
<SwitchPreferenceCompat
|
||||
android:key="game_bar_auto_enable"
|
||||
android:title="Auto-Enable GameBar for Selected Apps"
|
||||
android:summary="If turned on, selected apps will auto-enable GameBar even if the main switch is off"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<Preference
|
||||
android:key="game_bar_app_selector"
|
||||
android:title="Select Apps"
|
||||
android:summary="Add apps that should auto-enable GameBar" />
|
||||
|
||||
<Preference
|
||||
android:key="game_bar_app_remover"
|
||||
android:title="Remove Selected Apps"
|
||||
android:summary="Remove apps from the auto-enable list" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
71
parts/src/org/lineageos/settings/BootCompletedReceiver.java
Normal file
71
parts/src/org/lineageos/settings/BootCompletedReceiver.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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...");
|
||||
|
||||
}
|
||||
}
|
||||
95
parts/src/org/lineageos/settings/TileHandlerActivity.java
Normal file
95
parts/src/org/lineageos/settings/TileHandlerActivity.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.gamebar.GameBarSettingsActivity;
|
||||
import org.lineageos.settings.gamebar.GameBarTileService;
|
||||
import org.lineageos.settings.charge.ChargeActivity;
|
||||
import org.lineageos.settings.charge.ChargeQSTile;
|
||||
import org.lineageos.settings.charginginfo.ChargingInfoTileService;
|
||||
|
||||
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(GameBarTileService.class.getName(), GameBarSettingsActivity.class);
|
||||
TILE_ACTIVITY_MAP.put(ChargeQSTile.class.getName(), ChargeActivity.class);
|
||||
TILE_ACTIVITY_MAP.put(ChargingInfoTileService.class.getName(), ChargeActivity.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();
|
||||
}
|
||||
}
|
||||
45
parts/src/org/lineageos/settings/charge/ChargeActivity.java
Normal file
45
parts/src/org/lineageos/settings/charge/ChargeActivity.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
public class ChargeActivity extends CollapsingToolbarBaseActivity {
|
||||
|
||||
private static final String TAG_BYPASS_CHARGE = "bypass_charge";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getSupportFragmentManager().beginTransaction().replace(
|
||||
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
new ChargeSettingsFragment(), TAG_BYPASS_CHARGE).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class ChargeBatteryMonitorService extends Service {
|
||||
|
||||
private static final String TAG = "ChargeBatteryMonitorService";
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "charge_bypass_channel";
|
||||
private static final int NOTIFICATION_ID = 1001;
|
||||
|
||||
private ChargeUtils mChargeUtils;
|
||||
private BatteryReceiver mBatteryReceiver;
|
||||
private NotificationManager mNotificationManager;
|
||||
private boolean mIsMonitoring = false;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mChargeUtils = new ChargeUtils(this);
|
||||
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
createNotificationChannel();
|
||||
|
||||
Log.d(TAG, "Service created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (!mChargeUtils.isMasterToggleEnabled()) {
|
||||
Log.d(TAG, "Master toggle disabled, stopping service");
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if (!mIsMonitoring) {
|
||||
startMonitoring();
|
||||
}
|
||||
|
||||
return START_STICKY; // Restart service if killed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopMonitoring();
|
||||
Log.d(TAG, "Service destroyed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null; // We don't provide binding
|
||||
}
|
||||
|
||||
private void startMonitoring() {
|
||||
if (mIsMonitoring) return;
|
||||
|
||||
mBatteryReceiver = new BatteryReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
filter.addAction(Intent.ACTION_POWER_CONNECTED);
|
||||
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
|
||||
registerReceiver(mBatteryReceiver, filter);
|
||||
mIsMonitoring = true;
|
||||
|
||||
// Initial check
|
||||
mChargeUtils.updateBypassState();
|
||||
updateNotification();
|
||||
|
||||
Log.d(TAG, "Started battery monitoring");
|
||||
}
|
||||
|
||||
private void stopMonitoring() {
|
||||
if (!mIsMonitoring) return;
|
||||
|
||||
if (mBatteryReceiver != null) {
|
||||
unregisterReceiver(mBatteryReceiver);
|
||||
mBatteryReceiver = null;
|
||||
}
|
||||
|
||||
mIsMonitoring = false;
|
||||
stopForeground(true);
|
||||
|
||||
Log.d(TAG, "Stopped battery monitoring");
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
"Charging Bypass",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Charging bypass monitoring service");
|
||||
channel.setShowBadge(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
private void updateNotification() {
|
||||
if (!mIsMonitoring) return;
|
||||
|
||||
boolean bypassActive = mChargeUtils.isBypassChargeEnabled();
|
||||
int batteryLevel = mChargeUtils.getCurrentBatteryLevel();
|
||||
int mode = mChargeUtils.getBypassMode();
|
||||
|
||||
// If bypass is not active, hide notification completely regardless of mode
|
||||
if (!bypassActive) {
|
||||
stopForeground(true);
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
String title = getString(R.string.charge_bypass_notification_title);
|
||||
String text;
|
||||
|
||||
if (mode == ChargeUtils.MODE_SMART) {
|
||||
int target = mChargeUtils.getBatteryTarget();
|
||||
if (bypassActive) {
|
||||
text = getString(R.string.charge_bypass_smart_enabled, target);
|
||||
} else {
|
||||
text = "Smart mode active - Target: " + target + "% (Current: " + batteryLevel + "%)";
|
||||
}
|
||||
} else {
|
||||
text = getString(R.string.charge_bypass_notification_text);
|
||||
}
|
||||
|
||||
// Create a simple settings intent instead of ChargeActivity
|
||||
Intent settingsIntent = new Intent(android.provider.Settings.ACTION_SETTINGS);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
this, 0, settingsIntent, PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setSmallIcon(android.R.drawable.ic_menu_manage) // Use system icon as fallback
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setShowWhen(false)
|
||||
.build();
|
||||
|
||||
if (bypassActive) {
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private class BatteryReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (Intent.ACTION_BATTERY_CHANGED.equals(action) ||
|
||||
Intent.ACTION_POWER_CONNECTED.equals(action) ||
|
||||
Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
|
||||
|
||||
Log.d(TAG, "Battery state changed: " + action);
|
||||
|
||||
// Check if we should still be running
|
||||
if (!mChargeUtils.isMasterToggleEnabled()) {
|
||||
Log.d(TAG, "Master toggle disabled, stopping service");
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update bypass state based on current conditions
|
||||
boolean wasActive = mChargeUtils.isBypassChargeEnabled();
|
||||
mChargeUtils.updateBypassState();
|
||||
boolean isActive = mChargeUtils.isBypassChargeEnabled();
|
||||
|
||||
// Update notification
|
||||
updateNotification();
|
||||
|
||||
// Log state changes
|
||||
if (wasActive != isActive) {
|
||||
int batteryLevel = mChargeUtils.getCurrentBatteryLevel();
|
||||
Log.d(TAG, "Bypass state changed: " + isActive +
|
||||
" (Battery: " + batteryLevel + "%)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
parts/src/org/lineageos/settings/charge/ChargeQSTile.java
Normal file
121
parts/src/org/lineageos/settings/charge/ChargeQSTile.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.util.Log;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class ChargeQSTile extends TileService {
|
||||
|
||||
private static final String TAG = "ChargeQSTile";
|
||||
private ChargeUtils mChargeUtils;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mChargeUtils = new ChargeUtils(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
super.onStartListening();
|
||||
updateTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopListening() {
|
||||
super.onStopListening();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
super.onClick();
|
||||
|
||||
if (!mChargeUtils.canToggleManually()) {
|
||||
// Show inactive state if not in manual mode or master toggle is off
|
||||
updateTile();
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle manual bypass state
|
||||
boolean currentState = mChargeUtils.getManualBypassState();
|
||||
mChargeUtils.setManualBypassState(!currentState);
|
||||
|
||||
// Update tile immediately
|
||||
updateTile();
|
||||
|
||||
Log.d(TAG, "Manual bypass toggled to: " + !currentState);
|
||||
}
|
||||
|
||||
private void updateTile() {
|
||||
Tile tile = getQsTile();
|
||||
if (tile == null) return;
|
||||
|
||||
boolean masterEnabled = mChargeUtils.isMasterToggleEnabled();
|
||||
boolean isManualMode = mChargeUtils.getBypassMode() == ChargeUtils.MODE_MANUAL;
|
||||
boolean canToggle = masterEnabled && isManualMode;
|
||||
boolean bypassActive = mChargeUtils.isBypassChargeEnabled();
|
||||
|
||||
// Set tile icon
|
||||
Icon icon = Icon.createWithResource(this, R.drawable.ic_charge_bypass);
|
||||
tile.setIcon(icon);
|
||||
|
||||
// Set tile label
|
||||
tile.setLabel(getString(R.string.charge_bypass_qs_tile_label));
|
||||
|
||||
if (!masterEnabled) {
|
||||
// Master toggle is off
|
||||
tile.setState(Tile.STATE_UNAVAILABLE);
|
||||
tile.setSubtitle(getString(R.string.charge_bypass_qs_disabled));
|
||||
} else if (!isManualMode) {
|
||||
// In smart mode
|
||||
tile.setState(Tile.STATE_UNAVAILABLE);
|
||||
tile.setSubtitle("Smart Mode");
|
||||
} else if (canToggle) {
|
||||
// Manual mode and can toggle
|
||||
if (bypassActive) {
|
||||
tile.setState(Tile.STATE_ACTIVE);
|
||||
tile.setSubtitle(getString(R.string.charge_bypass_qs_active));
|
||||
} else {
|
||||
tile.setState(Tile.STATE_INACTIVE);
|
||||
tile.setSubtitle(getString(R.string.charge_bypass_qs_inactive));
|
||||
}
|
||||
} else {
|
||||
// Fallback state
|
||||
tile.setState(Tile.STATE_UNAVAILABLE);
|
||||
tile.setSubtitle(getString(R.string.charge_bypass_qs_disabled));
|
||||
}
|
||||
|
||||
tile.updateTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileAdded() {
|
||||
super.onTileAdded();
|
||||
updateTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileRemoved() {
|
||||
super.onTileRemoved();
|
||||
}
|
||||
}
|
||||
74
parts/src/org/lineageos/settings/charge/ChargeReceiver.java
Normal file
74
parts/src/org/lineageos/settings/charge/ChargeReceiver.java
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public class ChargeReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "ChargeReceiver";
|
||||
|
||||
@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)) {
|
||||
|
||||
Log.d(TAG, "Boot completed, restoring charge bypass settings");
|
||||
|
||||
ChargeUtils chargeUtils = new ChargeUtils(context);
|
||||
|
||||
// Check if bypass is supported on this device
|
||||
if (!chargeUtils.isBypassChargeSupported()) {
|
||||
Log.d(TAG, "Bypass not supported on this device");
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore bypass state based on saved preferences
|
||||
if (chargeUtils.isMasterToggleEnabled()) {
|
||||
Log.d(TAG, "Master toggle enabled, restoring bypass state");
|
||||
|
||||
// Reset bypass to disabled state first
|
||||
chargeUtils.enableBypassCharge(false);
|
||||
|
||||
// Start monitoring service if needed
|
||||
int mode = chargeUtils.getBypassMode();
|
||||
if (mode == ChargeUtils.MODE_SMART) {
|
||||
Intent serviceIntent = new Intent(context, ChargeBatteryMonitorService.class);
|
||||
context.startService(serviceIntent);
|
||||
Log.d(TAG, "Started battery monitoring service for smart mode");
|
||||
} else if (mode == ChargeUtils.MODE_MANUAL) {
|
||||
// In manual mode, respect the saved manual state
|
||||
// but don't automatically enable bypass on boot for safety
|
||||
chargeUtils.setManualBypassState(false);
|
||||
Log.d(TAG, "Manual mode restored, bypass disabled for safety");
|
||||
}
|
||||
|
||||
// Update bypass state based on current conditions
|
||||
chargeUtils.updateBypassState();
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Master toggle disabled, ensuring bypass is off");
|
||||
chargeUtils.enableBypassCharge(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
public class ChargeSettingsFragment extends PreferenceFragmentCompat
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "ChargeSettingsFragment";
|
||||
|
||||
// Preference keys
|
||||
private static final String KEY_BYPASS_CHARGE_MASTER = "bypass_charge_master";
|
||||
private static final String KEY_BYPASS_MODE_MANUAL = "bypass_mode_manual";
|
||||
private static final String KEY_BYPASS_MODE_SMART = "bypass_mode_smart";
|
||||
private static final String KEY_BATTERY_TARGET_SLIDER = "battery_target_slider";
|
||||
private static final String KEY_BYPASS_CONFIGURATION_CATEGORY = "bypass_configuration_category";
|
||||
|
||||
// Preferences
|
||||
private SwitchPreference mMasterToggle;
|
||||
private CheckBoxPreference mManualMode;
|
||||
private CheckBoxPreference mSmartMode;
|
||||
private MinMaxSeekBarPreference mBatteryTarget;
|
||||
private PreferenceCategory mConfigurationCategory;
|
||||
|
||||
private ChargeUtils mChargeUtils;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.charge_settings, rootKey);
|
||||
|
||||
mChargeUtils = new ChargeUtils(getActivity());
|
||||
|
||||
// Initialize preferences
|
||||
mMasterToggle = (SwitchPreference) findPreference(KEY_BYPASS_CHARGE_MASTER);
|
||||
mManualMode = (CheckBoxPreference) findPreference(KEY_BYPASS_MODE_MANUAL);
|
||||
mSmartMode = (CheckBoxPreference) findPreference(KEY_BYPASS_MODE_SMART);
|
||||
mBatteryTarget = (MinMaxSeekBarPreference) findPreference(KEY_BATTERY_TARGET_SLIDER);
|
||||
mConfigurationCategory = (PreferenceCategory) findPreference(KEY_BYPASS_CONFIGURATION_CATEGORY);
|
||||
|
||||
boolean bypassSupported = mChargeUtils.isBypassChargeSupported();
|
||||
|
||||
if (!bypassSupported) {
|
||||
// Disable all preferences if bypass is not supported
|
||||
mMasterToggle.setEnabled(false);
|
||||
mMasterToggle.setSummary(R.string.charge_bypass_unavailable);
|
||||
mConfigurationCategory.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up master toggle
|
||||
mMasterToggle.setChecked(mChargeUtils.isMasterToggleEnabled());
|
||||
mMasterToggle.setOnPreferenceChangeListener(this);
|
||||
|
||||
// Set up mode preferences
|
||||
int currentMode = mChargeUtils.getBypassMode();
|
||||
mManualMode.setChecked(currentMode == ChargeUtils.MODE_MANUAL);
|
||||
mSmartMode.setChecked(currentMode == ChargeUtils.MODE_SMART);
|
||||
mManualMode.setOnPreferenceChangeListener(this);
|
||||
mSmartMode.setOnPreferenceChangeListener(this);
|
||||
|
||||
// Set up battery target slider - Let the preference handle all mapping internally
|
||||
int batteryTarget = mChargeUtils.getBatteryTarget();
|
||||
mBatteryTarget.setBatteryTarget(batteryTarget); // Use the new method from MinMaxSeekBarPreference
|
||||
mBatteryTarget.setOnPreferenceChangeListener(this);
|
||||
updateBatteryTargetSummary(batteryTarget);
|
||||
|
||||
Log.d(TAG, "Initial battery target: " + batteryTarget + "%");
|
||||
|
||||
// Update UI state based on master toggle
|
||||
updateConfigurationVisibility(mChargeUtils.isMasterToggleEnabled());
|
||||
updateBatteryTargetVisibility(currentMode == ChargeUtils.MODE_SMART);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final String key = preference.getKey();
|
||||
|
||||
switch (key) {
|
||||
case KEY_BYPASS_CHARGE_MASTER:
|
||||
boolean masterEnabled = (Boolean) newValue;
|
||||
if (masterEnabled) {
|
||||
showMasterToggleWarning(() -> {
|
||||
mChargeUtils.setMasterToggleEnabled(true);
|
||||
updateConfigurationVisibility(true);
|
||||
startBatteryMonitorService();
|
||||
});
|
||||
return false; // Don't update UI until user confirms
|
||||
} else {
|
||||
mChargeUtils.setMasterToggleEnabled(false);
|
||||
mChargeUtils.enableBypassCharge(false); // Disable bypass immediately
|
||||
updateConfigurationVisibility(false);
|
||||
stopBatteryMonitorService();
|
||||
return true;
|
||||
}
|
||||
|
||||
case KEY_BYPASS_MODE_MANUAL:
|
||||
if ((Boolean) newValue) {
|
||||
mSmartMode.setChecked(false);
|
||||
mChargeUtils.setBypassMode(ChargeUtils.MODE_MANUAL);
|
||||
updateBatteryTargetVisibility(false);
|
||||
stopBatteryMonitorService();
|
||||
return true;
|
||||
}
|
||||
return false; // Don't allow unchecking without selecting another mode
|
||||
|
||||
case KEY_BYPASS_MODE_SMART:
|
||||
if ((Boolean) newValue) {
|
||||
mManualMode.setChecked(false);
|
||||
mChargeUtils.setBypassMode(ChargeUtils.MODE_SMART);
|
||||
updateBatteryTargetVisibility(true);
|
||||
startBatteryMonitorService();
|
||||
return true;
|
||||
}
|
||||
return false; // Don't allow unchecking without selecting another mode
|
||||
|
||||
case KEY_BATTERY_TARGET_SLIDER:
|
||||
// FIXED: Calculate the battery target based on the new slider value
|
||||
int sliderValue = (Integer) newValue;
|
||||
|
||||
// Create a temporary preference object to calculate the mapped value
|
||||
// since the preference hasn't been updated yet
|
||||
int actualBatteryTarget = mBatteryTarget.calculateBatteryTargetFromSlider(sliderValue);
|
||||
|
||||
Log.d(TAG, "Slider changed - Position: " + sliderValue + "%, Battery target: " + actualBatteryTarget + "%");
|
||||
|
||||
mChargeUtils.setBatteryTarget(actualBatteryTarget);
|
||||
updateBatteryTargetSummary(actualBatteryTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showMasterToggleWarning(Runnable onConfirm) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.charge_bypass_master_title)
|
||||
.setMessage(R.string.charge_bypass_warning)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
mMasterToggle.setChecked(true);
|
||||
if (onConfirm != null) {
|
||||
onConfirm.run();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
||||
mMasterToggle.setChecked(false);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void updateConfigurationVisibility(boolean visible) {
|
||||
mConfigurationCategory.setVisible(visible);
|
||||
if (visible) {
|
||||
int currentMode = mChargeUtils.getBypassMode();
|
||||
updateBatteryTargetVisibility(currentMode == ChargeUtils.MODE_SMART);
|
||||
} else {
|
||||
updateBatteryTargetVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBatteryTargetVisibility(boolean visible) {
|
||||
mBatteryTarget.setVisible(visible);
|
||||
}
|
||||
|
||||
private void updateBatteryTargetSummary(int value) {
|
||||
String summaryText = getString(R.string.charge_bypass_battery_target_summary, value);
|
||||
mBatteryTarget.setSummary(summaryText);
|
||||
}
|
||||
|
||||
private void startBatteryMonitorService() {
|
||||
Intent serviceIntent = new Intent(getActivity(), ChargeBatteryMonitorService.class);
|
||||
getActivity().startService(serviceIntent);
|
||||
}
|
||||
|
||||
private void stopBatteryMonitorService() {
|
||||
Intent serviceIntent = new Intent(getActivity(), ChargeBatteryMonitorService.class);
|
||||
getActivity().stopService(serviceIntent);
|
||||
}
|
||||
}
|
||||
239
parts/src/org/lineageos/settings/charge/ChargeUtils.java
Normal file
239
parts/src/org/lineageos/settings/charge/ChargeUtils.java
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.charge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.Log;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.lineageos.settings.utils.FileUtils;
|
||||
|
||||
public class ChargeUtils {
|
||||
|
||||
private static final String TAG = "ChargeUtils";
|
||||
|
||||
// Sysfs node for bypass control
|
||||
public static final String BYPASS_CHARGE_NODE = "/sys/class/power_supply/battery/input_suspend";
|
||||
|
||||
// Bypass modes
|
||||
public static final int MODE_MANUAL = 0;
|
||||
public static final int MODE_SMART = 1;
|
||||
|
||||
// Bypass states
|
||||
public static final int BYPASS_DISABLED = 0;
|
||||
public static final int BYPASS_ENABLED = 1;
|
||||
|
||||
// SharedPreferences keys
|
||||
private static final String PREF_MASTER_TOGGLE = "bypass_charge_master";
|
||||
private static final String PREF_BYPASS_MODE = "bypass_mode";
|
||||
private static final String PREF_BATTERY_TARGET = "battery_target";
|
||||
private static final String PREF_MANUAL_BYPASS_STATE = "manual_bypass_state";
|
||||
|
||||
// Default values
|
||||
private static final int DEFAULT_BATTERY_TARGET = 80;
|
||||
private static final int BATTERY_HYSTERESIS = 5; // 5% hysteresis for smart mode
|
||||
|
||||
private Context mContext;
|
||||
private SharedPreferences mSharedPrefs;
|
||||
|
||||
public ChargeUtils(Context context) {
|
||||
mContext = context;
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
// Master toggle methods
|
||||
public boolean isMasterToggleEnabled() {
|
||||
return mSharedPrefs.getBoolean(PREF_MASTER_TOGGLE, false);
|
||||
}
|
||||
|
||||
public void setMasterToggleEnabled(boolean enabled) {
|
||||
mSharedPrefs.edit().putBoolean(PREF_MASTER_TOGGLE, enabled).apply();
|
||||
if (!enabled) {
|
||||
// Disable bypass immediately when master toggle is turned off
|
||||
enableBypassCharge(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Mode management methods
|
||||
public int getBypassMode() {
|
||||
return mSharedPrefs.getInt(PREF_BYPASS_MODE, MODE_MANUAL);
|
||||
}
|
||||
|
||||
public void setBypassMode(int mode) {
|
||||
mSharedPrefs.edit().putInt(PREF_BYPASS_MODE, mode).apply();
|
||||
|
||||
// Reset bypass state when changing modes
|
||||
if (mode == MODE_MANUAL) {
|
||||
setManualBypassState(false);
|
||||
}
|
||||
|
||||
// Update bypass state based on new mode
|
||||
updateBypassState();
|
||||
}
|
||||
|
||||
// Battery target methods (for smart mode)
|
||||
public int getBatteryTarget() {
|
||||
return mSharedPrefs.getInt(PREF_BATTERY_TARGET, DEFAULT_BATTERY_TARGET);
|
||||
}
|
||||
|
||||
public void setBatteryTarget(int target) {
|
||||
mSharedPrefs.edit().putInt(PREF_BATTERY_TARGET, target).apply();
|
||||
// Immediately update bypass state if we're in smart mode
|
||||
if (getBypassMode() == MODE_SMART) {
|
||||
updateBypassState();
|
||||
}
|
||||
}
|
||||
|
||||
// Manual bypass state methods
|
||||
public boolean getManualBypassState() {
|
||||
return mSharedPrefs.getBoolean(PREF_MANUAL_BYPASS_STATE, false);
|
||||
}
|
||||
|
||||
public void setManualBypassState(boolean enabled) {
|
||||
mSharedPrefs.edit().putBoolean(PREF_MANUAL_BYPASS_STATE, enabled).apply();
|
||||
if (getBypassMode() == MODE_MANUAL) {
|
||||
updateBypassState();
|
||||
}
|
||||
}
|
||||
|
||||
// Core bypass control methods
|
||||
public boolean isBypassChargeEnabled() {
|
||||
try {
|
||||
String value = FileUtils.readOneLine(BYPASS_CHARGE_NODE);
|
||||
return value != null && value.equals("1");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to read bypass charge status", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void enableBypassCharge(boolean enable) {
|
||||
try {
|
||||
FileUtils.writeLine(BYPASS_CHARGE_NODE, enable ? "1" : "0");
|
||||
Log.d(TAG, "Bypass charge " + (enable ? "enabled" : "disabled"));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to write bypass charge status", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Support check methods
|
||||
public boolean isNodeAccessible(String node) {
|
||||
try {
|
||||
FileUtils.readOneLine(node);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Node " + node + " not accessible", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBypassChargeSupported() {
|
||||
return isNodeAccessible(BYPASS_CHARGE_NODE);
|
||||
}
|
||||
|
||||
// Main logic for updating bypass state
|
||||
public void updateBypassState() {
|
||||
if (!isMasterToggleEnabled()) {
|
||||
enableBypassCharge(false);
|
||||
return;
|
||||
}
|
||||
|
||||
int mode = getBypassMode();
|
||||
boolean shouldBypass = false;
|
||||
|
||||
switch (mode) {
|
||||
case MODE_MANUAL:
|
||||
shouldBypass = getManualBypassState();
|
||||
break;
|
||||
|
||||
case MODE_SMART:
|
||||
shouldBypass = shouldBypassInSmartMode();
|
||||
break;
|
||||
}
|
||||
|
||||
enableBypassCharge(shouldBypass);
|
||||
}
|
||||
|
||||
// Smart mode logic
|
||||
private boolean shouldBypassInSmartMode() {
|
||||
int currentBattery = getCurrentBatteryLevel();
|
||||
int target = getBatteryTarget();
|
||||
boolean currentlyBypassed = isBypassChargeEnabled();
|
||||
|
||||
if (currentBattery >= target && !currentlyBypassed) {
|
||||
// Enable bypass when we reach target
|
||||
return true;
|
||||
} else if (currentBattery <= (target - BATTERY_HYSTERESIS) && currentlyBypassed) {
|
||||
// Disable bypass when we drop below target minus hysteresis
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep current state if we're between thresholds
|
||||
return currentlyBypassed;
|
||||
}
|
||||
|
||||
// Battery level helper
|
||||
public int getCurrentBatteryLevel() {
|
||||
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent batteryStatus = mContext.registerReceiver(null, ifilter);
|
||||
|
||||
if (batteryStatus != null) {
|
||||
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
|
||||
if (level != -1 && scale != -1) {
|
||||
return (int) ((level / (float) scale) * 100);
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // Unknown battery level
|
||||
}
|
||||
|
||||
// Charging state helper
|
||||
public boolean isCharging() {
|
||||
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent batteryStatus = mContext.registerReceiver(null, ifilter);
|
||||
|
||||
if (batteryStatus != null) {
|
||||
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
|
||||
return status == BatteryManager.BATTERY_STATUS_CHARGING ||
|
||||
status == BatteryManager.BATTERY_STATUS_FULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Utility methods for external components
|
||||
public String getBypassModeString() {
|
||||
switch (getBypassMode()) {
|
||||
case MODE_MANUAL:
|
||||
return "Manual";
|
||||
case MODE_SMART:
|
||||
return "Smart";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canToggleManually() {
|
||||
return isMasterToggleEnabled() && getBypassMode() == MODE_MANUAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package org.lineageos.settings.charginginfo
|
||||
|
||||
import org.lineageos.settings.R
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.BatteryManager
|
||||
import android.service.quicksettings.Tile
|
||||
import android.service.quicksettings.TileService
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
|
||||
class ChargingInfoTileService : TileService() {
|
||||
private var updateTimer: Timer? = null
|
||||
private var batteryReceiver: BroadcastReceiver? = null
|
||||
private var isCharging = false
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ChargingTileService"
|
||||
private const val UPDATE_INTERVAL = 2000L // Update every 2 seconds
|
||||
|
||||
// Battery sysfs paths
|
||||
private const val CURRENT_NOW_PATH = "/sys/class/power_supply/battery/current_now"
|
||||
private const val VOLTAGE_NOW_PATH = "/sys/class/power_supply/battery/voltage_now"
|
||||
private const val STATUS_PATH = "/sys/class/power_supply/battery/status"
|
||||
}
|
||||
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
Log.d(TAG, "Tile started listening")
|
||||
|
||||
// Register battery receiver
|
||||
registerBatteryReceiver()
|
||||
|
||||
// Start periodic updates
|
||||
startPeriodicUpdates()
|
||||
|
||||
// Initial update
|
||||
updateTile()
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
super.onStopListening()
|
||||
Log.d(TAG, "Tile stopped listening")
|
||||
|
||||
// Stop updates
|
||||
stopPeriodicUpdates()
|
||||
|
||||
// Unregister receiver
|
||||
unregisterBatteryReceiver()
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
// Force update when clicked
|
||||
updateTile()
|
||||
}
|
||||
|
||||
private fun registerBatteryReceiver() {
|
||||
if (batteryReceiver == null) {
|
||||
batteryReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_BATTERY_CHANGED,
|
||||
Intent.ACTION_POWER_CONNECTED,
|
||||
Intent.ACTION_POWER_DISCONNECTED -> {
|
||||
updateChargingStatus(intent)
|
||||
updateTile()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val filter = IntentFilter().apply {
|
||||
addAction(Intent.ACTION_BATTERY_CHANGED)
|
||||
addAction(Intent.ACTION_POWER_CONNECTED)
|
||||
addAction(Intent.ACTION_POWER_DISCONNECTED)
|
||||
}
|
||||
|
||||
registerReceiver(batteryReceiver, filter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterBatteryReceiver() {
|
||||
batteryReceiver?.let {
|
||||
try {
|
||||
unregisterReceiver(it)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.w(TAG, "Receiver not registered")
|
||||
}
|
||||
batteryReceiver = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPeriodicUpdates() {
|
||||
stopPeriodicUpdates()
|
||||
updateTimer = Timer().apply {
|
||||
scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
if (isCharging) {
|
||||
updateTile()
|
||||
}
|
||||
}
|
||||
}, 0, UPDATE_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopPeriodicUpdates() {
|
||||
updateTimer?.cancel()
|
||||
updateTimer = null
|
||||
}
|
||||
|
||||
private fun updateChargingStatus(intent: Intent) {
|
||||
val status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
|
||||
isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
|
||||
}
|
||||
|
||||
private fun updateTile() {
|
||||
val tile = qsTile ?: return
|
||||
|
||||
try {
|
||||
val chargingInfo = getChargingInfo()
|
||||
|
||||
tile.apply {
|
||||
when {
|
||||
chargingInfo.isCharging -> {
|
||||
state = Tile.STATE_ACTIVE
|
||||
label = "Charging"
|
||||
subtitle = "${chargingInfo.power}W"
|
||||
contentDescription = "Charging at ${chargingInfo.power}W (${chargingInfo.current}A, ${chargingInfo.voltage}V)"
|
||||
icon = Icon.createWithResource(this@ChargingInfoTileService,
|
||||
R.drawable.ic_charging_speed)
|
||||
}
|
||||
chargingInfo.isDischarging -> {
|
||||
state = Tile.STATE_INACTIVE
|
||||
label = "Discharging"
|
||||
subtitle = "${chargingInfo.power}W"
|
||||
contentDescription = "Discharging at ${chargingInfo.power}W"
|
||||
icon = Icon.createWithResource(this@ChargingInfoTileService,
|
||||
R.drawable.ic_battery_discharge)
|
||||
}
|
||||
else -> {
|
||||
state = Tile.STATE_UNAVAILABLE
|
||||
label = "Not Charging"
|
||||
subtitle = "0W"
|
||||
contentDescription = "Battery not charging or discharging"
|
||||
icon = Icon.createWithResource(this@ChargingInfoTileService,
|
||||
R.drawable.ic_battery_unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile.updateTile()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error updating tile", e)
|
||||
|
||||
// Fallback tile state
|
||||
tile.apply {
|
||||
state = Tile.STATE_UNAVAILABLE
|
||||
label = "Error"
|
||||
subtitle = "N/A"
|
||||
contentDescription = "Unable to read charging information"
|
||||
icon = Icon.createWithResource(this@ChargingInfoTileService,
|
||||
R.drawable.ic_error)
|
||||
}
|
||||
tile.updateTile()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getChargingInfo(): ChargingInfo {
|
||||
val currentNow = readSysfsValue(CURRENT_NOW_PATH)
|
||||
val voltageNow = readSysfsValue(VOLTAGE_NOW_PATH)
|
||||
val status = readSysfsString(STATUS_PATH)
|
||||
|
||||
// Convert from milliamps to amps and microvolts to volts
|
||||
val currentA = currentNow / 1000.0 // mA → A
|
||||
val voltageV = voltageNow / 1000000.0 // µV → V
|
||||
|
||||
// Calculate power in watts
|
||||
val powerW = Math.abs(currentA * voltageV)
|
||||
|
||||
val isCharging = status.equals("Charging", ignoreCase = true) && currentNow > 0
|
||||
val isDischarging = currentNow < 0
|
||||
|
||||
return ChargingInfo(
|
||||
current = String.format("%.1f", Math.abs(currentA)),
|
||||
voltage = String.format("%.1f", voltageV),
|
||||
power = String.format("%.1f", powerW),
|
||||
isCharging = isCharging,
|
||||
isDischarging = isDischarging
|
||||
)
|
||||
}
|
||||
|
||||
private fun readSysfsValue(path: String): Long {
|
||||
return try {
|
||||
File(path).readText().trim().toLong()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to read $path", e)
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
private fun readSysfsString(path: String): String {
|
||||
return try {
|
||||
File(path).readText().trim()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to read $path", e)
|
||||
"Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
data class ChargingInfo(
|
||||
val current: String,
|
||||
val voltage: String,
|
||||
val power: String,
|
||||
val isCharging: Boolean,
|
||||
val isDischarging: Boolean
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.lineageos.settings.charge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import androidx.preference.SeekBarPreference;
|
||||
|
||||
public class MinMaxSeekBarPreference extends SeekBarPreference {
|
||||
private static final String TAG = "MinMaxSeekBarPreference";
|
||||
|
||||
private int mMinValue = 0;
|
||||
private int mMaxValue = 100;
|
||||
|
||||
public MinMaxSeekBarPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// Get min/max values from XML attributes
|
||||
if (attrs != null) {
|
||||
for (int i = 0; i < attrs.getAttributeCount(); i++) {
|
||||
String attrName = attrs.getAttributeName(i);
|
||||
if ("min".equals(attrName)) {
|
||||
mMinValue = attrs.getAttributeIntValue(i, 0);
|
||||
} else if ("max".equals(attrName)) {
|
||||
mMaxValue = attrs.getAttributeIntValue(i, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the SeekBar to use full 0-100 range for UI, but map values internally
|
||||
setMin(0);
|
||||
setMax(100);
|
||||
|
||||
Log.d(TAG, "MinMaxSeekBarPreference initialized - Min: " + mMinValue + ", Max: " + mMaxValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(Object defaultValue) {
|
||||
int value = defaultValue == null ? 80 : (Integer) defaultValue;
|
||||
// Ensure value is within bounds
|
||||
value = Math.max(mMinValue, Math.min(mMaxValue, value));
|
||||
|
||||
// Convert the actual battery target value to slider position
|
||||
int sliderPosition = valueToSliderPosition(value);
|
||||
super.setValue(sliderPosition);
|
||||
|
||||
Log.i(TAG, "Battery target initial value set: " + value + "% (Slider position: " + sliderPosition + "%)");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||
int defaultValue = a.getInt(index, 80);
|
||||
Log.d(TAG, "Getting default battery target value: " + defaultValue + "%");
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(int seekBarValue) {
|
||||
// seekBarValue here is the slider position (0-100)
|
||||
// Just store it as-is since the slider represents the UI position
|
||||
super.setValue(seekBarValue);
|
||||
|
||||
// Calculate and log the actual mapped battery target
|
||||
int mappedValue = sliderPositionToValue(seekBarValue);
|
||||
Log.i(TAG, "Battery target changed - Slider: " + seekBarValue + "%, Actual target: " + mappedValue + "% (Range: " + mMinValue + "-" + mMaxValue + "%)");
|
||||
}
|
||||
|
||||
// Convert slider position (0-100) to actual battery target value
|
||||
private int sliderPositionToValue(int sliderPosition) {
|
||||
return mMinValue + ((sliderPosition * (mMaxValue - mMinValue)) / 100);
|
||||
}
|
||||
|
||||
// Convert actual battery target value to slider position (0-100)
|
||||
private int valueToSliderPosition(int value) {
|
||||
if (mMaxValue == mMinValue) return 0; // Avoid division by zero
|
||||
return ((value - mMinValue) * 100) / (mMaxValue - mMinValue);
|
||||
}
|
||||
|
||||
// Get the actual mapped battery target value
|
||||
public int getBatteryTarget() {
|
||||
int sliderValue = getValue();
|
||||
return sliderPositionToValue(sliderValue);
|
||||
}
|
||||
|
||||
// Set the battery target value (converts to appropriate slider position)
|
||||
public void setBatteryTarget(int targetValue) {
|
||||
targetValue = Math.max(mMinValue, Math.min(mMaxValue, targetValue));
|
||||
int sliderPosition = valueToSliderPosition(targetValue);
|
||||
setValue(sliderPosition);
|
||||
}
|
||||
|
||||
// Calculate battery target from a given slider position (used during preference change)
|
||||
public int calculateBatteryTargetFromSlider(int sliderPosition) {
|
||||
return sliderPositionToValue(sliderPosition);
|
||||
}
|
||||
}
|
||||
@@ -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.gamebar;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
774
parts/src/org/lineageos/settings/gamebar/GameBar.java
Normal file
774
parts/src/org/lineageos/settings/gamebar/GameBar.java
Normal file
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
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 GameBar {
|
||||
|
||||
private static GameBar sInstance;
|
||||
public static synchronized GameBar getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new GameBar(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_bar_x";
|
||||
private static final String PREF_KEY_Y = "game_bar_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 mOverlayFormat = "full";
|
||||
private String mPosition = "top_left";
|
||||
private String mSplitMode = "stacked";
|
||||
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 GameBar(Context context) {
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mBgDrawable = new GradientDrawable();
|
||||
applyBackgroundStyle();
|
||||
|
||||
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";
|
||||
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||
.edit()
|
||||
.putString("game_bar_format", mOverlayFormat)
|
||||
.apply();
|
||||
Toast.makeText(mContext, "Overlay Format: " + mOverlayFormat, Toast.LENGTH_SHORT).show();
|
||||
updateStats();
|
||||
return true;
|
||||
}
|
||||
return super.onSingleTapConfirmed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void applyPreferences() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
|
||||
mShowFps = prefs.getBoolean("game_bar_fps_enable", false);
|
||||
mShowBatteryTemp = prefs.getBoolean("game_bar_temp_enable", false);
|
||||
mShowCpuUsage = prefs.getBoolean("game_bar_cpu_usage_enable", false);
|
||||
mShowCpuClock = prefs.getBoolean("game_bar_cpu_clock_enable", false);
|
||||
mShowCpuTemp = prefs.getBoolean("game_bar_cpu_temp_enable", false);
|
||||
mShowRam = prefs.getBoolean("game_bar_ram_enable", false);
|
||||
|
||||
mShowGpuUsage = prefs.getBoolean("game_bar_gpu_usage_enable", false);
|
||||
mShowGpuClock = prefs.getBoolean("game_bar_gpu_clock_enable", false);
|
||||
mShowGpuTemp = prefs.getBoolean("game_bar_gpu_temp_enable", false);
|
||||
|
||||
mDoubleTapCaptureEnabled = prefs.getBoolean("game_bar_doubletap_capture", false);
|
||||
mSingleTapToggleEnabled = prefs.getBoolean("game_bar_single_tap_toggle", false);
|
||||
|
||||
updateSplitMode(prefs.getString("game_bar_split_mode", "stacked"));
|
||||
updateTextSize(prefs.getInt("game_bar_text_size", 16));
|
||||
updateBackgroundAlpha(prefs.getInt("game_bar_background_alpha", 128));
|
||||
updateCornerRadius(prefs.getInt("game_bar_corner_radius", 16));
|
||||
updatePadding(prefs.getInt("game_bar_padding", 12));
|
||||
updateTitleColor(prefs.getString("game_bar_title_color", "#FFFFFF"));
|
||||
updateValueColor(prefs.getString("game_bar_value_color", "#4CAF50"));
|
||||
updateOverlayFormat(prefs.getString("game_bar_format", "full"));
|
||||
updateUpdateInterval(prefs.getString("game_bar_update_interval", "1000"));
|
||||
updatePosition(prefs.getString("game_bar_position", "top_left"));
|
||||
|
||||
int spacing = prefs.getInt("game_bar_item_spacing", 8);
|
||||
updateItemSpacing(spacing);
|
||||
|
||||
mLongPressEnabled = prefs.getBoolean("game_bar_longpress_enable", false);
|
||||
String lpTimeoutStr = prefs.getString("game_bar_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();
|
||||
|
||||
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();
|
||||
|
||||
// Start the FPS meter if using the new API method.
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
||||
GameBarFpsMeter.getInstance(mContext).start();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
||||
GameBarFpsMeter.getInstance(mContext).stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
if (!mIsShowing || mRootLayout == null) return;
|
||||
|
||||
mRootLayout.removeAllViews();
|
||||
|
||||
List<View> statViews = new ArrayList<>();
|
||||
|
||||
// 1) FPS
|
||||
float fpsVal = GameBarFpsMeter.getInstance(mContext).getFps();
|
||||
String fpsStr = fpsVal >= 0 ? String.format(Locale.getDefault(), "%.0f", 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 = GameBarCpuInfo.getCpuUsage();
|
||||
String display = "N/A".equals(cpuUsageStr) ? "N/A" : cpuUsageStr + "%";
|
||||
statViews.add(createStatLine("CPU", display));
|
||||
}
|
||||
|
||||
// 4) CPU freq
|
||||
if (mShowCpuClock) {
|
||||
List<String> freqs = GameBarCpuInfo.getCpuFrequencies();
|
||||
if (!freqs.isEmpty()) {
|
||||
statViews.add(buildCpuFreqView(freqs));
|
||||
}
|
||||
}
|
||||
|
||||
// 5) CPU temp
|
||||
String cpuTempStr = "N/A";
|
||||
if (mShowCpuTemp) {
|
||||
cpuTempStr = GameBarCpuInfo.getCpuTemp();
|
||||
statViews.add(createStatLine("CPU Temp", "N/A".equals(cpuTempStr) ? "N/A" : cpuTempStr + "°C"));
|
||||
}
|
||||
|
||||
// 6) RAM usage
|
||||
String ramStr = "N/A";
|
||||
if (mShowRam) {
|
||||
ramStr = GameBarMemInfo.getRamUsage();
|
||||
statViews.add(createStatLine("RAM", "N/A".equals(ramStr) ? "N/A" : ramStr + " MB"));
|
||||
}
|
||||
|
||||
// 7) GPU usage
|
||||
String gpuUsageStr = "N/A";
|
||||
if (mShowGpuUsage) {
|
||||
gpuUsageStr = GameBarGpuInfo.getGpuUsage();
|
||||
statViews.add(createStatLine("GPU", "N/A".equals(gpuUsageStr) ? "N/A" : gpuUsageStr + "%"));
|
||||
}
|
||||
|
||||
// 8) GPU clock
|
||||
String gpuClockStr = "N/A";
|
||||
if (mShowGpuClock) {
|
||||
gpuClockStr = GameBarGpuInfo.getGpuClock();
|
||||
statViews.add(createStatLine("GPU Freq", "N/A".equals(gpuClockStr) ? "N/A" : gpuClockStr + "MHz"));
|
||||
}
|
||||
|
||||
// 9) GPU temp
|
||||
String gpuTempStr = "N/A";
|
||||
if (mShowGpuTemp) {
|
||||
gpuTempStr = GameBarGpuInfo.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,
|
||||
fpsStr,
|
||||
batteryTempStr,
|
||||
cpuUsageStr,
|
||||
cpuTempStr,
|
||||
gpuUsageStr,
|
||||
gpuClockStr,
|
||||
gpuTempStr
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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, GameBarSettingsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mContext.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
// Exception ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static int dpToPx(Context context, int dp) {
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * scale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameBarAppRemoverActivity extends CollapsingToolbarBaseActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game_bar_app_selector);
|
||||
setTitle("Remove Auto-Enable Apps");
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.content_frame, new GameBarAppRemoverFragment())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
import org.lineageos.settings.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameBarAppRemoverFragment extends Fragment {
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private GameBarAutoAppsAdapter adapter;
|
||||
private PackageManager packageManager;
|
||||
private List<ApplicationInfo> autoAppsList;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.game_bar_app_selector, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
recyclerView = view.findViewById(R.id.app_list);
|
||||
packageManager = getContext().getPackageManager();
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
loadAutoApps();
|
||||
}
|
||||
|
||||
private void loadAutoApps() {
|
||||
Set<String> autoAppsSet = getSavedAutoApps();
|
||||
autoAppsList = new ArrayList<>();
|
||||
for (String pkg : autoAppsSet) {
|
||||
try {
|
||||
ApplicationInfo info = packageManager.getApplicationInfo(pkg, 0);
|
||||
autoAppsList.add(info);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
adapter = new GameBarAutoAppsAdapter(packageManager, autoAppsList, new GameBarAutoAppsAdapter.OnAppRemoveListener() {
|
||||
@Override
|
||||
public void onAppRemove(ApplicationInfo appInfo) {
|
||||
removeAppFromAutoList(appInfo.packageName);
|
||||
Toast.makeText(getContext(), appInfo.loadLabel(packageManager) + " removed.", Toast.LENGTH_SHORT).show();
|
||||
autoAppsList.remove(appInfo);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private Set<String> getSavedAutoApps() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.getStringSet(GameBarAppSelectorFragment.PREF_AUTO_APPS, new HashSet<>());
|
||||
}
|
||||
|
||||
private void removeAppFromAutoList(String packageName) {
|
||||
Set<String> autoApps = new HashSet<>(getSavedAutoApps());
|
||||
autoApps.remove(packageName);
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.edit().putStringSet(GameBarAppSelectorFragment.PREF_AUTO_APPS, autoApps).apply();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameBarAppSelectorActivity extends CollapsingToolbarBaseActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game_bar_app_selector);
|
||||
setTitle("Select Apps for GameBar");
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.content_frame, new GameBarAppSelectorFragment())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
import org.lineageos.settings.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameBarAppSelectorFragment extends Fragment {
|
||||
|
||||
public static final String PREF_AUTO_APPS = "game_bar_auto_apps";
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private GameBarAppsAdapter adapter;
|
||||
private PackageManager packageManager;
|
||||
private List<ApplicationInfo> allApps;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.game_bar_app_selector, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
recyclerView = view.findViewById(R.id.app_list);
|
||||
packageManager = getContext().getPackageManager();
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
loadApps();
|
||||
}
|
||||
|
||||
private void loadApps() {
|
||||
allApps = new ArrayList<>();
|
||||
List<ApplicationInfo> installedApps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
Set<String> autoApps = getSavedAutoApps();
|
||||
for (ApplicationInfo appInfo : installedApps) {
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 &&
|
||||
!appInfo.packageName.equals(getContext().getPackageName()) &&
|
||||
!autoApps.contains(appInfo.packageName)) {
|
||||
allApps.add(appInfo);
|
||||
}
|
||||
}
|
||||
adapter = new GameBarAppsAdapter(packageManager, allApps, new GameBarAppsAdapter.OnAppClickListener() {
|
||||
@Override
|
||||
public void onAppClick(ApplicationInfo appInfo) {
|
||||
addAppToAutoList(appInfo.packageName);
|
||||
Toast.makeText(getContext(), appInfo.loadLabel(packageManager) + " added.", Toast.LENGTH_SHORT).show();
|
||||
allApps.remove(appInfo);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
recyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private Set<String> getSavedAutoApps() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.getStringSet(PREF_AUTO_APPS, new HashSet<>());
|
||||
}
|
||||
|
||||
private void addAppToAutoList(String packageName) {
|
||||
Set<String> autoApps = new HashSet<>(getSavedAutoApps());
|
||||
autoApps.add(packageName);
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.edit().putStringSet(PREF_AUTO_APPS, autoApps).apply();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.lineageos.settings.R;
|
||||
import java.util.List;
|
||||
|
||||
public class GameBarAppsAdapter extends RecyclerView.Adapter<GameBarAppsAdapter.ViewHolder> {
|
||||
|
||||
public interface OnAppClickListener {
|
||||
void onAppClick(ApplicationInfo appInfo);
|
||||
}
|
||||
|
||||
private PackageManager packageManager;
|
||||
private List<ApplicationInfo> apps;
|
||||
private OnAppClickListener listener;
|
||||
|
||||
public GameBarAppsAdapter(PackageManager packageManager, List<ApplicationInfo> apps, OnAppClickListener listener) {
|
||||
this.packageManager = packageManager;
|
||||
this.apps = apps;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GameBarAppsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.game_bar_app_selector_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GameBarAppsAdapter.ViewHolder holder, int position) {
|
||||
final ApplicationInfo appInfo = apps.get(position);
|
||||
holder.appName.setText(appInfo.loadLabel(packageManager));
|
||||
holder.appPackage.setText(appInfo.packageName);
|
||||
holder.appIcon.setImageDrawable(appInfo.loadIcon(packageManager));
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onAppClick(appInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return apps.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView appName;
|
||||
TextView appPackage;
|
||||
ImageView appIcon;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
appName = itemView.findViewById(R.id.app_name);
|
||||
appPackage = itemView.findViewById(R.id.app_package);
|
||||
appIcon = itemView.findViewById(R.id.app_icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.lineageos.settings.R;
|
||||
import java.util.List;
|
||||
|
||||
public class GameBarAutoAppsAdapter extends RecyclerView.Adapter<GameBarAutoAppsAdapter.ViewHolder> {
|
||||
|
||||
public interface OnAppRemoveListener {
|
||||
void onAppRemove(ApplicationInfo appInfo);
|
||||
}
|
||||
|
||||
private PackageManager packageManager;
|
||||
private List<ApplicationInfo> apps;
|
||||
private OnAppRemoveListener listener;
|
||||
|
||||
public GameBarAutoAppsAdapter(PackageManager packageManager, List<ApplicationInfo> apps, OnAppRemoveListener listener) {
|
||||
this.packageManager = packageManager;
|
||||
this.apps = apps;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GameBarAutoAppsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.game_bar_app_selector_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GameBarAutoAppsAdapter.ViewHolder holder, int position) {
|
||||
final ApplicationInfo appInfo = apps.get(position);
|
||||
holder.appName.setText(appInfo.loadLabel(packageManager));
|
||||
holder.appPackage.setText(appInfo.packageName);
|
||||
holder.appIcon.setImageDrawable(appInfo.loadIcon(packageManager));
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onAppRemove(appInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return apps.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView appName;
|
||||
TextView appPackage;
|
||||
ImageView appIcon;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
appName = itemView.findViewById(R.id.app_name);
|
||||
appPackage = itemView.findViewById(R.id.app_package);
|
||||
appIcon = itemView.findViewById(R.id.app_icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public class GameBarBootReceiver 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 mainEnabled = prefs.getBoolean("game_bar_enable", false);
|
||||
boolean autoEnabled = prefs.getBoolean("game_bar_auto_enable", false);
|
||||
if (mainEnabled) {
|
||||
GameBar.getInstance(context).applyPreferences();
|
||||
GameBar.getInstance(context).show();
|
||||
}
|
||||
if (autoEnabled) {
|
||||
Intent monitorIntent = new Intent(context, GameBarMonitorService.class);
|
||||
context.startService(monitorIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
parts/src/org/lineageos/settings/gamebar/GameBarCpuInfo.java
Normal file
130
parts/src/org/lineageos/settings/gamebar/GameBarCpuInfo.java
Normal file
@@ -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.gamebar;
|
||||
|
||||
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 GameBarCpuInfo {
|
||||
|
||||
private static long sPrevIdle = -1;
|
||||
private static long sPrevTotal = -1;
|
||||
|
||||
private static final String CPU_TEMP_PATH = "/sys/class/thermal/thermal_zone19/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(GameBarCpuInfo::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
187
parts/src/org/lineageos/settings/gamebar/GameBarFpsMeter.java
Normal file
187
parts/src/org/lineageos/settings/gamebar/GameBarFpsMeter.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TaskFpsCallback;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class GameBarFpsMeter {
|
||||
|
||||
private static final float TOLERANCE = 0.1f;
|
||||
private static final long STALENESS_THRESHOLD_MS = 2000;
|
||||
private static final long TASK_CHECK_INTERVAL_MS = 1000;
|
||||
|
||||
private static GameBarFpsMeter sInstance;
|
||||
private final Context mContext;
|
||||
private final WindowManager mWindowManager;
|
||||
private final SharedPreferences mPrefs;
|
||||
private float mCurrentFps = 0f;
|
||||
private TaskFpsCallback mTaskFpsCallback;
|
||||
private boolean mCallbackRegistered = false;
|
||||
private int mCurrentTaskId = -1;
|
||||
private long mLastFpsUpdateTime = System.currentTimeMillis();
|
||||
private final android.os.Handler mHandler = new android.os.Handler();
|
||||
|
||||
public static synchronized GameBarFpsMeter getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new GameBarFpsMeter(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private GameBarFpsMeter(Context context) {
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
mTaskFpsCallback = new TaskFpsCallback() {
|
||||
@Override
|
||||
public void onFpsReported(float fps) {
|
||||
if (fps > 0) {
|
||||
mCurrentFps = fps;
|
||||
mLastFpsUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
String method = mPrefs.getString("game_bar_fps_method", "new");
|
||||
if (!"new".equals(method)) return;
|
||||
|
||||
stop();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
int taskId = getFocusedTaskId();
|
||||
if (taskId <= 0) {
|
||||
return;
|
||||
}
|
||||
mCurrentTaskId = taskId;
|
||||
try {
|
||||
mWindowManager.registerTaskFpsCallback(mCurrentTaskId, Runnable::run, mTaskFpsCallback);
|
||||
mCallbackRegistered = true;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
mLastFpsUpdateTime = System.currentTimeMillis();
|
||||
mHandler.postDelayed(mTaskCheckRunnable, TASK_CHECK_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
String method = mPrefs.getString("game_bar_fps_method", "new");
|
||||
if ("new".equals(method) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (mCallbackRegistered) {
|
||||
try {
|
||||
mWindowManager.unregisterTaskFpsCallback(mTaskFpsCallback);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
mCallbackRegistered = false;
|
||||
}
|
||||
mHandler.removeCallbacks(mTaskCheckRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
public float getFps() {
|
||||
String method = mPrefs.getString("game_bar_fps_method", "new");
|
||||
if ("legacy".equals(method)) {
|
||||
return readLegacyFps();
|
||||
} else {
|
||||
return mCurrentFps;
|
||||
}
|
||||
}
|
||||
|
||||
private float readLegacyFps() {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader("/sys/class/drm/sde-crtc-0/measured_fps"))) {
|
||||
String line = br.readLine();
|
||||
if (line != null && line.startsWith("fps:")) {
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
return Float.parseFloat(parts[1].trim());
|
||||
}
|
||||
}
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
private final Runnable mTaskCheckRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
int newTaskId = getFocusedTaskId();
|
||||
if (newTaskId > 0 && newTaskId != mCurrentTaskId) {
|
||||
reinitCallback();
|
||||
} else {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - mLastFpsUpdateTime > STALENESS_THRESHOLD_MS) {
|
||||
reinitCallback();
|
||||
}
|
||||
}
|
||||
mHandler.postDelayed(this, TASK_CHECK_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int getFocusedTaskId() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
Class<?> atmClass = Class.forName("android.app.ActivityTaskManager");
|
||||
Method getServiceMethod = atmClass.getDeclaredMethod("getService");
|
||||
Object atmService = getServiceMethod.invoke(null);
|
||||
Method getFocusedRootTaskInfoMethod = atmService.getClass().getMethod("getFocusedRootTaskInfo");
|
||||
Object taskInfo = getFocusedRootTaskInfoMethod.invoke(atmService);
|
||||
if (taskInfo != null) {
|
||||
try {
|
||||
Field taskIdField = taskInfo.getClass().getField("taskId");
|
||||
return taskIdField.getInt(taskInfo);
|
||||
} catch (NoSuchFieldException nsfe) {
|
||||
try {
|
||||
Field taskIdField = taskInfo.getClass().getField("mTaskId");
|
||||
return taskIdField.getInt(taskInfo);
|
||||
} catch (NoSuchFieldException nsfe2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void reinitCallback() {
|
||||
stop();
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
start();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
394
parts/src/org/lineageos/settings/gamebar/GameBarFragment.java
Normal file
394
parts/src/org/lineageos/settings/gamebar/GameBarFragment.java
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
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 androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameBarFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private GameBar mGameBar;
|
||||
private MainSwitchPreference mMasterSwitch;
|
||||
private SwitchPreferenceCompat mAutoEnableSwitch;
|
||||
private SwitchPreferenceCompat mFpsSwitch;
|
||||
private SwitchPreferenceCompat mBatteryTempSwitch;
|
||||
private SwitchPreferenceCompat mCpuUsageSwitch;
|
||||
private SwitchPreferenceCompat mCpuClockSwitch;
|
||||
private SwitchPreferenceCompat mCpuTempSwitch;
|
||||
private SwitchPreferenceCompat mRamSwitch;
|
||||
private SwitchPreferenceCompat mGpuUsageSwitch;
|
||||
private SwitchPreferenceCompat mGpuClockSwitch;
|
||||
private SwitchPreferenceCompat mGpuTempSwitch;
|
||||
private Preference mCaptureStartPref;
|
||||
private Preference mCaptureStopPref;
|
||||
private Preference mCaptureExportPref;
|
||||
private SwitchPreferenceCompat mDoubleTapCapturePref;
|
||||
private SwitchPreferenceCompat mSingleTapTogglePref;
|
||||
private SwitchPreferenceCompat 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_bar_preferences, rootKey);
|
||||
|
||||
mGameBar = GameBar.getInstance(getContext());
|
||||
|
||||
// Initialize all preferences.
|
||||
mMasterSwitch = findPreference("game_bar_enable");
|
||||
mAutoEnableSwitch = findPreference("game_bar_auto_enable");
|
||||
mFpsSwitch = findPreference("game_bar_fps_enable");
|
||||
mBatteryTempSwitch = findPreference("game_bar_temp_enable");
|
||||
mCpuUsageSwitch = findPreference("game_bar_cpu_usage_enable");
|
||||
mCpuClockSwitch = findPreference("game_bar_cpu_clock_enable");
|
||||
mCpuTempSwitch = findPreference("game_bar_cpu_temp_enable");
|
||||
mRamSwitch = findPreference("game_bar_ram_enable");
|
||||
mGpuUsageSwitch = findPreference("game_bar_gpu_usage_enable");
|
||||
mGpuClockSwitch = findPreference("game_bar_gpu_clock_enable");
|
||||
mGpuTempSwitch = findPreference("game_bar_gpu_temp_enable");
|
||||
|
||||
mCaptureStartPref = findPreference("game_bar_capture_start");
|
||||
mCaptureStopPref = findPreference("game_bar_capture_stop");
|
||||
mCaptureExportPref = findPreference("game_bar_capture_export");
|
||||
|
||||
mDoubleTapCapturePref = findPreference("game_bar_doubletap_capture");
|
||||
mSingleTapTogglePref = findPreference("game_bar_single_tap_toggle");
|
||||
mLongPressEnablePref = findPreference("game_bar_longpress_enable");
|
||||
mLongPressTimeoutPref = findPreference("game_bar_longpress_timeout");
|
||||
|
||||
mTextSizePref = findPreference("game_bar_text_size");
|
||||
mBgAlphaPref = findPreference("game_bar_background_alpha");
|
||||
mCornerRadiusPref = findPreference("game_bar_corner_radius");
|
||||
mPaddingPref = findPreference("game_bar_padding");
|
||||
mItemSpacingPref = findPreference("game_bar_item_spacing");
|
||||
|
||||
mUpdateIntervalPref = findPreference("game_bar_update_interval");
|
||||
mTextColorPref = findPreference("game_bar_text_color");
|
||||
mTitleColorPref = findPreference("game_bar_title_color");
|
||||
mValueColorPref = findPreference("game_bar_value_color");
|
||||
mPositionPref = findPreference("game_bar_position");
|
||||
mSplitModePref = findPreference("game_bar_split_mode");
|
||||
mOverlayFormatPref = findPreference("game_bar_format");
|
||||
|
||||
Preference appSelectorPref = findPreference("game_bar_app_selector");
|
||||
if (appSelectorPref != null) {
|
||||
appSelectorPref.setOnPreferenceClickListener(pref -> {
|
||||
Intent intent = new Intent(getContext(), GameBarAppSelectorActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
Preference appRemoverPref = findPreference("game_bar_app_remover");
|
||||
if (appRemoverPref != null) {
|
||||
appRemoverPref.setOnPreferenceClickListener(pref -> {
|
||||
Intent intent = new Intent(getContext(), GameBarAppRemoverActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mMasterSwitch != null) {
|
||||
mMasterSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
boolean enabled = (boolean) newValue;
|
||||
if (enabled) {
|
||||
if (Settings.canDrawOverlays(getContext())) {
|
||||
mGameBar.applyPreferences();
|
||||
mGameBar.show();
|
||||
getContext().startService(new Intent(getContext(), GameBarMonitorService.class));
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.overlay_permission_required, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mGameBar.hide();
|
||||
if (mAutoEnableSwitch == null || !mAutoEnableSwitch.isChecked()) {
|
||||
getContext().stopService(new Intent(getContext(), GameBarMonitorService.class));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mAutoEnableSwitch != null) {
|
||||
mAutoEnableSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
boolean autoEnabled = (boolean) newValue;
|
||||
if (autoEnabled) {
|
||||
getContext().startService(new Intent(getContext(), GameBarMonitorService.class));
|
||||
} else {
|
||||
if (mMasterSwitch == null || !mMasterSwitch.isChecked()) {
|
||||
getContext().stopService(new Intent(getContext(), GameBarMonitorService.class));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (mFpsSwitch != null) {
|
||||
mFpsSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowFps((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mBatteryTempSwitch != null) {
|
||||
mBatteryTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowBatteryTemp((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuUsageSwitch != null) {
|
||||
mCpuUsageSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowCpuUsage((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuClockSwitch != null) {
|
||||
mCpuClockSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowCpuClock((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCpuTempSwitch != null) {
|
||||
mCpuTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowCpuTemp((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mRamSwitch != null) {
|
||||
mRamSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowRam((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mGpuUsageSwitch != null) {
|
||||
mGpuUsageSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowGpuUsage((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mGpuClockSwitch != null) {
|
||||
mGpuClockSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setShowGpuClock((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mGpuTempSwitch != null) {
|
||||
mGpuTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.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) -> {
|
||||
mGameBar.setDoubleTapCaptureEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mSingleTapTogglePref != null) {
|
||||
mSingleTapTogglePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setSingleTapToggleEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mLongPressEnablePref != null) {
|
||||
mLongPressEnablePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
mGameBar.setLongPressEnabled((boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mLongPressTimeoutPref != null) {
|
||||
mLongPressTimeoutPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
long ms = Long.parseLong((String) newValue);
|
||||
mGameBar.setLongPressThresholdMs(ms);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mTextSizePref != null) {
|
||||
mTextSizePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mGameBar.updateTextSize((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mBgAlphaPref != null) {
|
||||
mBgAlphaPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mGameBar.updateBackgroundAlpha((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mCornerRadiusPref != null) {
|
||||
mCornerRadiusPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mGameBar.updateCornerRadius((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mPaddingPref != null) {
|
||||
mPaddingPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mGameBar.updatePadding((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mItemSpacingPref != null) {
|
||||
mItemSpacingPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Integer) {
|
||||
mGameBar.updateItemSpacing((Integer) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mUpdateIntervalPref != null) {
|
||||
mUpdateIntervalPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updateUpdateInterval((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mTextColorPref != null) {
|
||||
mTextColorPref.setOnPreferenceChangeListener((pref, newValue) -> true);
|
||||
}
|
||||
if (mTitleColorPref != null) {
|
||||
mTitleColorPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updateTitleColor((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mValueColorPref != null) {
|
||||
mValueColorPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updateValueColor((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mPositionPref != null) {
|
||||
mPositionPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updatePosition((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mSplitModePref != null) {
|
||||
mSplitModePref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updateSplitMode((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (mOverlayFormatPref != null) {
|
||||
mOverlayFormatPref.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
mGameBar.updateOverlayFormat((String) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!hasUsageStatsPermission(requireContext())) {
|
||||
requestUsageStatsPermission();
|
||||
}
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
if ((mMasterSwitch != null && mMasterSwitch.isChecked()) ||
|
||||
(mAutoEnableSwitch != null && mAutoEnableSwitch.isChecked())) {
|
||||
context.startService(new Intent(context, GameBarMonitorService.class));
|
||||
} else {
|
||||
context.stopService(new Intent(context, GameBarMonitorService.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
80
parts/src/org/lineageos/settings/gamebar/GameBarGpuInfo.java
Normal file
80
parts/src/org/lineageos/settings/gamebar/GameBarGpuInfo.java
Normal file
@@ -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.gamebar;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GameBarGpuInfo {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
parts/src/org/lineageos/settings/gamebar/GameBarMemInfo.java
Normal file
65
parts/src/org/lineageos/settings/gamebar/GameBarMemInfo.java
Normal file
@@ -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.gamebar;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GameBarMemInfo {
|
||||
|
||||
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,82 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameBarMonitorService extends Service {
|
||||
|
||||
private Handler mHandler;
|
||||
private Runnable mMonitorRunnable;
|
||||
private static final long MONITOR_INTERVAL = 2000; // 2 seconds
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mHandler = new Handler();
|
||||
mMonitorRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
monitorForegroundApp();
|
||||
mHandler.postDelayed(this, MONITOR_INTERVAL);
|
||||
}
|
||||
};
|
||||
mHandler.post(mMonitorRunnable);
|
||||
}
|
||||
|
||||
private void monitorForegroundApp() {
|
||||
var prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean masterEnabled = prefs.getBoolean("game_bar_enable", false);
|
||||
if (masterEnabled) {
|
||||
GameBar.getInstance(this).applyPreferences();
|
||||
GameBar.getInstance(this).show();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean autoEnabled = prefs.getBoolean("game_bar_auto_enable", false);
|
||||
if (!autoEnabled) {
|
||||
GameBar.getInstance(this).hide();
|
||||
return;
|
||||
}
|
||||
|
||||
String foreground = ForegroundAppDetector.getForegroundPackageName(this);
|
||||
Set<String> autoApps = prefs.getStringSet(GameBarAppSelectorFragment.PREF_AUTO_APPS, new HashSet<>());
|
||||
if (autoApps.contains(foreground)) {
|
||||
GameBar.getInstance(this).applyPreferences();
|
||||
GameBar.getInstance(this).show();
|
||||
} else {
|
||||
GameBar.getInstance(this).hide();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mHandler.removeCallbacks(mMonitorRunnable);
|
||||
}
|
||||
}
|
||||
@@ -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.gamebar;
|
||||
|
||||
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 GameBarSettingsActivity 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_bar);
|
||||
setTitle(getString(R.string.game_bar_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,72 @@
|
||||
/*
|
||||
* 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.gamebar;
|
||||
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.lineageos.settings.R;
|
||||
|
||||
public class GameBarTileService extends TileService {
|
||||
private GameBar mGameBar;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mGameBar = GameBar.getInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
boolean enabled = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("game_bar_enable", false);
|
||||
updateTileState(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
boolean currentlyEnabled = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("game_bar_enable", false);
|
||||
boolean newState = !currentlyEnabled;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.edit()
|
||||
.putBoolean("game_bar_enable", newState)
|
||||
.commit();
|
||||
|
||||
updateTileState(newState);
|
||||
|
||||
if (newState) {
|
||||
mGameBar.applyPreferences();
|
||||
mGameBar.show();
|
||||
} else {
|
||||
mGameBar.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_bar_tile_label));
|
||||
tile.setContentDescription(getString(R.string.game_bar_tile_description));
|
||||
tile.updateTile();
|
||||
}
|
||||
}
|
||||
132
parts/src/org/lineageos/settings/gamebar/GameDataExport.java
Normal file
132
parts/src/org/lineageos/settings/gamebar/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.gamebar;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
# Charger
|
||||
ro.charger.enable_suspend=true
|
||||
|
||||
# Display
|
||||
debug.sf.hw=0
|
||||
debug.egl.hw=0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
rootdir/etc/vendor/init.qcom.rc
vendored
13
rootdir/etc/vendor/init.qcom.rc
vendored
@@ -122,6 +122,14 @@ on early-boot
|
||||
exec u:r:vendor_qti_init_shell:s0 -- /vendor/bin/init.qti.can.sh
|
||||
setprop ro.sf.lcd_density ${vendor.display.lcd_density}
|
||||
|
||||
# SSR
|
||||
write /sys/bus/msm_subsys/devices/subsys0/restart_level RELATED
|
||||
write /sys/bus/msm_subsys/devices/subsys1/restart_level RELATED
|
||||
write /sys/bus/msm_subsys/devices/subsys2/restart_level RELATED
|
||||
write /sys/bus/msm_subsys/devices/subsys3/restart_level RELATED
|
||||
write /sys/bus/msm_subsys/devices/subsys4/restart_level RELATED
|
||||
write /sys/bus/msm_subsys/devices/subsys5/restart_level RELATED
|
||||
|
||||
chown system system /mnt/vendor/persist/data/pfm/licenses
|
||||
chmod 0775 /mnt/vendor/persist/data/pfm/licenses
|
||||
|
||||
@@ -560,6 +568,11 @@ on property:ro.crypto.state=unsupported
|
||||
on property:persist.env.fastdorm.enabled=true
|
||||
setprop persist.radio.data_no_toggle 1
|
||||
|
||||
on post-fs-data
|
||||
# Charge Bypass
|
||||
chown system system /sys/class/power_supply/battery
|
||||
chmod 0660 /sys/class/power_supply/battery
|
||||
|
||||
service vendor.qrtr-ns /vendor/bin/qrtr-ns -f
|
||||
class core
|
||||
user vendor_qrtr
|
||||
|
||||
6
rootdir/etc/vendor/init.sm7125.rc
vendored
6
rootdir/etc/vendor/init.sm7125.rc
vendored
@@ -29,6 +29,12 @@ on boot
|
||||
chown system system /sys/class/leds/vibrator/vmax_mv
|
||||
chmod 0644 /sys/class/leds/vibrator/vmax_mv
|
||||
|
||||
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
|
||||
|
||||
on property:ro.boot.product.hardware.sku=nfc
|
||||
start secure_element_hal_service
|
||||
start vendor.nfc_hal_service
|
||||
|
||||
116
rootdir/etc/vendor/init.target.rc
vendored
116
rootdir/etc/vendor/init.target.rc
vendored
@@ -87,122 +87,6 @@ on init
|
||||
chown cameraserver cameraserver /dev/cpuset/camera-daemon/tasks
|
||||
chmod 0660 /dev/cpuset/camera-daemon/tasks
|
||||
|
||||
on late-fs
|
||||
# Start services for bootanim
|
||||
start servicemanager
|
||||
start surfaceflinger
|
||||
start bootanim
|
||||
|
||||
on property:sys.boot_completed=1
|
||||
# Runtime fs tuning
|
||||
write /sys/block/sda/queue/nr_requests 128
|
||||
write /sys/block/sda/queue/iostats 0
|
||||
|
||||
# Block layer tuning: discard chunk size up to 128MB
|
||||
# Otherwise, contiguous discards can be merged
|
||||
write /sys/block/sda/queue/discard_max_bytes 134217728
|
||||
|
||||
# Back to default VM settings
|
||||
write /proc/sys/vm/dirty_expire_centisecs 3000
|
||||
write /proc/sys/vm/dirty_background_ratio 10
|
||||
|
||||
# Setup runtime cpusets
|
||||
write /dev/cpuset/top-app/cpus 0-7
|
||||
write /dev/cpuset/foreground/cpus 0-2,5-7
|
||||
write /dev/cpuset/background/cpus 0-3
|
||||
write /dev/cpuset/system-background/cpus 0-3
|
||||
write /dev/cpuset/restricted/cpus 0-3
|
||||
|
||||
# Setup runtime schedTune
|
||||
write /dev/stune/foreground/schedtune.prefer_idle 1
|
||||
write /dev/stune/foreground/schedtune.prefer_high_cap 0
|
||||
write /dev/stune/foreground/schedtune.boost 0
|
||||
write /dev/stune/schedtune.prefer_idle 0
|
||||
write /dev/stune/schedtune.prefer_high_cap 0
|
||||
write /dev/stune/schedtune.boost 0
|
||||
write /dev/stune/top-app/schedtune.prefer_idle 1
|
||||
write /dev/stune/top-app/schedtune.prefer_high_cap 0
|
||||
write /dev/stune/top-app/schedtune.boost 10
|
||||
|
||||
# Setup runtime blkio
|
||||
# value for group_idle is us
|
||||
write /dev/blkio/blkio.weight 1000
|
||||
write /dev/blkio/background/blkio.weight 200
|
||||
write /dev/blkio/blkio.group_idle 2000
|
||||
write /dev/blkio/background/blkio.group_idle 0
|
||||
|
||||
on early-init
|
||||
# configure governor settings
|
||||
write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor schedutil
|
||||
write /sys/devices/system/cpu/cpu0/cpufreq/schedutil/up_rate_limit_us 500
|
||||
write /sys/devices/system/cpu/cpu0/cpufreq/schedutil/down_rate_limit_us 20000
|
||||
|
||||
# configure governor settings for big cluster
|
||||
write /sys/devices/system/cpu/cpu4/cpufreq/scaling_governor schedutil
|
||||
write /sys/devices/system/cpu/cpu4/cpufreq/schedutil/up_rate_limit_us 500
|
||||
write /sys/devices/system/cpu/cpu4/cpufreq/schedutil/down_rate_limit_us 20000
|
||||
|
||||
# Disable UFS clock scaling
|
||||
write /sys/bus/platform/devices/${ro.boot.bootdevice}/clkscale_enable 0
|
||||
|
||||
on init
|
||||
# Boot time cpuset and stune
|
||||
write /dev/cpuset/top-app/cpus 0-7
|
||||
write /dev/cpuset/foreground/cpus 0-7
|
||||
write /dev/cpuset/background/cpus 0-5
|
||||
write /dev/cpuset/system-background/cpus 0-5
|
||||
write /dev/cpuset/restricted/cpus 2-5
|
||||
write /dev/stune/foreground/schedtune.prefer_idle 1
|
||||
write /dev/stune/foreground/schedtune.prefer_high_cap 1
|
||||
write /dev/stune/foreground/schedtune.boost 100
|
||||
write /dev/stune/schedtune.prefer_idle 1
|
||||
write /dev/stune/schedtune.prefer_high_cap 1
|
||||
write /dev/stune/schedtune.boost 100
|
||||
write /dev/stune/top-app/schedtune.prefer_idle 1
|
||||
write /dev/stune/top-app/schedtune.prefer_high_cap 1
|
||||
write /dev/stune/top-app/schedtune.boost 100
|
||||
|
||||
# Boot time fs tuning
|
||||
write /sys/block/sda/queue/iostats 0
|
||||
write /sys/block/sda/queue/scheduler cfq
|
||||
write /sys/block/sda/queue/iosched/slice_idle 0
|
||||
write /sys/block/sda/queue/nr_requests 256
|
||||
|
||||
# Disable UFS powersaving
|
||||
write /sys/devices/platform/soc/${ro.boot.bootdevice}/clkgate_enable 0
|
||||
|
||||
# Disable powersaving
|
||||
write /sys/module/lpm_levels/parameters/sleep_disabled 1
|
||||
|
||||
# bring back all cores
|
||||
write /sys/devices/system/cpu/cpu0/online 1
|
||||
write /sys/devices/system/cpu/cpu1/online 1
|
||||
write /sys/devices/system/cpu/cpu2/online 1
|
||||
write /sys/devices/system/cpu/cpu3/online 1
|
||||
write /sys/devices/system/cpu/cpu4/online 1
|
||||
write /sys/devices/system/cpu/cpu5/online 1
|
||||
write /sys/devices/system/cpu/cpu6/online 1
|
||||
write /sys/devices/system/cpu/cpu7/online 1
|
||||
|
||||
mkdir /dev/cpuset/camera-daemon-dedicated
|
||||
write /dev/cpuset/camera-daemon-dedicated/cpus 0-7
|
||||
write /dev/cpuset/camera-daemon-dedicated/mems 0
|
||||
chown system system /dev/cpuset/camera-daemon-dedicated
|
||||
chown system system /dev/cpuset/camera-daemon-dedicated/tasks
|
||||
chmod 0664 /dev/cpuset/camera-daemon-dedicated/tasks
|
||||
|
||||
# create an stune group for camera-specific processes
|
||||
mkdir /dev/stune/camera-daemon
|
||||
write /dev/stune/camera-daemon/schedtune.prefer_idle 1
|
||||
write /dev/stune/camera-daemon/schedtune.boost 0
|
||||
chown system system /dev/stune/camera-daemon
|
||||
chown system system /dev/stune/camera-daemon/tasks
|
||||
chmod 0664 /dev/stune/camera-daemon/tasks
|
||||
|
||||
# set default schedTune value for camera-daemon
|
||||
write /dev/stune/camera-daemon/schedtune.prefer_idle 1
|
||||
write /dev/stune/camera-daemon/schedtune.boost 0
|
||||
|
||||
on early-fs
|
||||
start vold
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2019 The Android Open Source 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.
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.sensors@2.0-service.multihal_realme_sm7125",
|
||||
stem: "android.hardware.sensors@2.0-service.multihal",
|
||||
defaults: [
|
||||
"hidl_defaults",
|
||||
],
|
||||
vendor: true,
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"HalProxy.cpp",
|
||||
"HalProxyCallback.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
|
||||
vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
|
||||
header_libs: [
|
||||
"android.hardware.sensors@2.X-multihal.header",
|
||||
"android.hardware.sensors@2.X-shared-utils",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.sensors@2.0",
|
||||
"android.hardware.sensors@2.0-ScopedWakelock",
|
||||
"android.hardware.sensors@2.1",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libpower",
|
||||
"libutils",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.sensors@1.0-convert",
|
||||
],
|
||||
}
|
||||
@@ -1,769 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#include "HalProxy.h"
|
||||
|
||||
#include <android/hardware/sensors/2.0/types.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include "hardware_legacy/power.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace sensors {
|
||||
namespace V2_1 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::sensors::V1_0::Result;
|
||||
using ::android::hardware::sensors::V2_0::EventQueueFlagBits;
|
||||
using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
|
||||
using ::android::hardware::sensors::V2_0::implementation::getTimeNow;
|
||||
using ::android::hardware::sensors::V2_0::implementation::kWakelockTimeoutNs;
|
||||
|
||||
typedef V2_0::implementation::ISensorsSubHal*(SensorsHalGetSubHalFunc)(uint32_t*);
|
||||
typedef V2_1::implementation::ISensorsSubHal*(SensorsHalGetSubHalV2_1Func)(uint32_t*);
|
||||
|
||||
static constexpr int32_t kBitsAfterSubHalIndex = 24;
|
||||
|
||||
/**
|
||||
* Set the subhal index as first byte of sensor handle and return this modified version.
|
||||
*
|
||||
* @param sensorHandle The sensor handle to modify.
|
||||
* @param subHalIndex The index in the hal proxy of the sub hal this sensor belongs to.
|
||||
*
|
||||
* @return The modified sensor handle.
|
||||
*/
|
||||
int32_t setSubHalIndex(int32_t sensorHandle, size_t subHalIndex) {
|
||||
return sensorHandle | (static_cast<int32_t>(subHalIndex) << kBitsAfterSubHalIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the subHalIndex from sensorHandle.
|
||||
*
|
||||
* @param sensorHandle The sensorHandle to extract from.
|
||||
*
|
||||
* @return The subhal index.
|
||||
*/
|
||||
size_t extractSubHalIndex(int32_t sensorHandle) {
|
||||
return static_cast<size_t>(sensorHandle >> kBitsAfterSubHalIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert nanoseconds to milliseconds.
|
||||
*
|
||||
* @param nanos The nanoseconds input.
|
||||
*
|
||||
* @return The milliseconds count.
|
||||
*/
|
||||
int64_t msFromNs(int64_t nanos) {
|
||||
constexpr int64_t nanosecondsInAMillsecond = 1000000;
|
||||
return nanos / nanosecondsInAMillsecond;
|
||||
}
|
||||
|
||||
HalProxy::HalProxy() {
|
||||
const char* kMultiHalConfigFile = "/vendor/etc/sensors/hals.conf";
|
||||
initializeSubHalListFromConfigFile(kMultiHalConfigFile);
|
||||
init();
|
||||
}
|
||||
|
||||
HalProxy::HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList) {
|
||||
for (ISensorsSubHalV2_0* subHal : subHalList) {
|
||||
mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
HalProxy::HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList,
|
||||
std::vector<ISensorsSubHalV2_1*>& subHalListV2_1) {
|
||||
for (ISensorsSubHalV2_0* subHal : subHalList) {
|
||||
mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
|
||||
}
|
||||
|
||||
for (ISensorsSubHalV2_1* subHal : subHalListV2_1) {
|
||||
mSubHalList.push_back(std::make_unique<SubHalWrapperV2_1>(subHal));
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
HalProxy::~HalProxy() {
|
||||
stopThreads();
|
||||
}
|
||||
|
||||
Return<void> HalProxy::getSensorsList_2_1(ISensorsV2_1::getSensorsList_2_1_cb _hidl_cb) {
|
||||
std::vector<V2_1::SensorInfo> sensors;
|
||||
for (const auto& iter : mSensors) {
|
||||
sensors.push_back(iter.second);
|
||||
}
|
||||
_hidl_cb(sensors);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> HalProxy::getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) {
|
||||
std::vector<V1_0::SensorInfo> sensors;
|
||||
for (const auto& iter : mSensors) {
|
||||
sensors.push_back(convertToOldSensorInfo(iter.second));
|
||||
}
|
||||
_hidl_cb(sensors);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::setOperationMode(OperationMode mode) {
|
||||
Result result = Result::OK;
|
||||
size_t subHalIndex;
|
||||
for (subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
|
||||
result = mSubHalList[subHalIndex]->setOperationMode(mode);
|
||||
if (result != Result::OK) {
|
||||
ALOGE("setOperationMode failed for SubHal: %s",
|
||||
mSubHalList[subHalIndex]->getName().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != Result::OK) {
|
||||
// Reset the subhal operation modes that have been flipped
|
||||
for (size_t i = 0; i < subHalIndex; i++) {
|
||||
mSubHalList[i]->setOperationMode(mCurrentOperationMode);
|
||||
}
|
||||
} else {
|
||||
mCurrentOperationMode = mode;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::activate(int32_t sensorHandle, bool enabled) {
|
||||
if (!isSubHalIndexValid(sensorHandle)) {
|
||||
return Result::BAD_VALUE;
|
||||
}
|
||||
return getSubHalForSensorHandle(sensorHandle)
|
||||
->activate(clearSubHalIndex(sensorHandle), enabled);
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::initialize_2_1(
|
||||
const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
|
||||
const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
|
||||
const sp<V2_1::ISensorsCallback>& sensorsCallback) {
|
||||
sp<ISensorsCallbackWrapperBase> dynamicCallback =
|
||||
new ISensorsCallbackWrapperV2_1(sensorsCallback);
|
||||
|
||||
// Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
|
||||
auto eventQueue =
|
||||
std::make_unique<EventMessageQueueV2_1>(eventQueueDescriptor, true /* resetPointers */);
|
||||
std::unique_ptr<EventMessageQueueWrapperBase> queue =
|
||||
std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
|
||||
|
||||
// Create the Wake Lock FMQ from the wakeLockDescriptor. Reset the read/write positions.
|
||||
auto hidlWakeLockQueue =
|
||||
std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor, true /* resetPointers */);
|
||||
std::unique_ptr<WakeLockMessageQueueWrapperBase> wakeLockQueue =
|
||||
std::make_unique<WakeLockMessageQueueWrapperHidl>(hidlWakeLockQueue);
|
||||
|
||||
return initializeCommon(queue, wakeLockQueue, dynamicCallback);
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::initialize(
|
||||
const ::android::hardware::MQDescriptorSync<V1_0::Event>& eventQueueDescriptor,
|
||||
const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
|
||||
const sp<V2_0::ISensorsCallback>& sensorsCallback) {
|
||||
sp<ISensorsCallbackWrapperBase> dynamicCallback =
|
||||
new ISensorsCallbackWrapperV2_0(sensorsCallback);
|
||||
|
||||
// Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
|
||||
auto eventQueue =
|
||||
std::make_unique<EventMessageQueueV2_0>(eventQueueDescriptor, true /* resetPointers */);
|
||||
std::unique_ptr<EventMessageQueueWrapperBase> queue =
|
||||
std::make_unique<EventMessageQueueWrapperV1_0>(eventQueue);
|
||||
|
||||
// Create the Wake Lock FMQ from the wakeLockDescriptor. Reset the read/write positions.
|
||||
auto hidlWakeLockQueue =
|
||||
std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor, true /* resetPointers */);
|
||||
std::unique_ptr<WakeLockMessageQueueWrapperBase> wakeLockQueue =
|
||||
std::make_unique<WakeLockMessageQueueWrapperHidl>(hidlWakeLockQueue);
|
||||
|
||||
return initializeCommon(queue, wakeLockQueue, dynamicCallback);
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::initializeCommon(
|
||||
std::unique_ptr<EventMessageQueueWrapperBase>& eventQueue,
|
||||
std::unique_ptr<WakeLockMessageQueueWrapperBase>& wakeLockQueue,
|
||||
const sp<ISensorsCallbackWrapperBase>& sensorsCallback) {
|
||||
Result result = Result::OK;
|
||||
|
||||
stopThreads();
|
||||
resetSharedWakelock();
|
||||
|
||||
// So that the pending write events queue can be cleared safely and when we start threads
|
||||
// again we do not get new events until after initialize resets the subhals.
|
||||
disableAllSensors();
|
||||
|
||||
// Clears the queue if any events were pending write before.
|
||||
mPendingWriteEventsQueue = std::queue<std::pair<std::vector<V2_1::Event>, size_t>>();
|
||||
mSizePendingWriteEventsQueue = 0;
|
||||
|
||||
// Clears previously connected dynamic sensors
|
||||
mDynamicSensors.clear();
|
||||
|
||||
mDynamicSensorsCallback = sensorsCallback;
|
||||
|
||||
// Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
|
||||
mEventQueue = std::move(eventQueue);
|
||||
|
||||
// Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
|
||||
// events have been successfully read and handled by the framework.
|
||||
mWakeLockQueue = std::move(wakeLockQueue);
|
||||
|
||||
if (mEventQueueFlag != nullptr) {
|
||||
EventFlag::deleteEventFlag(&mEventQueueFlag);
|
||||
}
|
||||
if (mWakelockQueueFlag != nullptr) {
|
||||
EventFlag::deleteEventFlag(&mWakelockQueueFlag);
|
||||
}
|
||||
if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
|
||||
result = Result::BAD_VALUE;
|
||||
}
|
||||
if (EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakelockQueueFlag) != OK) {
|
||||
result = Result::BAD_VALUE;
|
||||
}
|
||||
if (!mDynamicSensorsCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
|
||||
result = Result::BAD_VALUE;
|
||||
}
|
||||
|
||||
mThreadsRun.store(true);
|
||||
|
||||
mPendingWritesThread = std::thread(startPendingWritesThread, this);
|
||||
mWakelockThread = std::thread(startWakelockThread, this);
|
||||
|
||||
for (size_t i = 0; i < mSubHalList.size(); i++) {
|
||||
Result currRes = mSubHalList[i]->initialize(this, this, i);
|
||||
if (currRes != Result::OK) {
|
||||
result = currRes;
|
||||
ALOGE("Subhal '%s' failed to initialize.", mSubHalList[i]->getName().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentOperationMode = OperationMode::NORMAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
|
||||
int64_t maxReportLatencyNs) {
|
||||
if (!isSubHalIndexValid(sensorHandle)) {
|
||||
return Result::BAD_VALUE;
|
||||
}
|
||||
return getSubHalForSensorHandle(sensorHandle)
|
||||
->batch(clearSubHalIndex(sensorHandle), samplingPeriodNs, maxReportLatencyNs);
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::flush(int32_t sensorHandle) {
|
||||
if (!isSubHalIndexValid(sensorHandle)) {
|
||||
return Result::BAD_VALUE;
|
||||
}
|
||||
return getSubHalForSensorHandle(sensorHandle)->flush(clearSubHalIndex(sensorHandle));
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::injectSensorData_2_1(const V2_1::Event& event) {
|
||||
return injectSensorData(convertToOldEvent(event));
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::injectSensorData(const V1_0::Event& event) {
|
||||
Result result = Result::OK;
|
||||
if (mCurrentOperationMode == OperationMode::NORMAL &&
|
||||
event.sensorType != V1_0::SensorType::ADDITIONAL_INFO) {
|
||||
ALOGE("An event with type != ADDITIONAL_INFO passed to injectSensorData while operation"
|
||||
" mode was NORMAL.");
|
||||
result = Result::BAD_VALUE;
|
||||
}
|
||||
if (result == Result::OK) {
|
||||
V1_0::Event subHalEvent = event;
|
||||
if (!isSubHalIndexValid(event.sensorHandle)) {
|
||||
return Result::BAD_VALUE;
|
||||
}
|
||||
subHalEvent.sensorHandle = clearSubHalIndex(event.sensorHandle);
|
||||
result = getSubHalForSensorHandle(event.sensorHandle)
|
||||
->injectSensorData(convertToNewEvent(subHalEvent));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<void> HalProxy::registerDirectChannel(const SharedMemInfo& mem,
|
||||
ISensorsV2_0::registerDirectChannel_cb _hidl_cb) {
|
||||
if (mDirectChannelSubHal == nullptr) {
|
||||
_hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
|
||||
} else {
|
||||
mDirectChannelSubHal->registerDirectChannel(mem, _hidl_cb);
|
||||
}
|
||||
return Return<void>();
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::unregisterDirectChannel(int32_t channelHandle) {
|
||||
Result result;
|
||||
if (mDirectChannelSubHal == nullptr) {
|
||||
result = Result::INVALID_OPERATION;
|
||||
} else {
|
||||
result = mDirectChannelSubHal->unregisterDirectChannel(channelHandle);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<void> HalProxy::configDirectReport(int32_t sensorHandle, int32_t channelHandle,
|
||||
RateLevel rate,
|
||||
ISensorsV2_0::configDirectReport_cb _hidl_cb) {
|
||||
if (mDirectChannelSubHal == nullptr) {
|
||||
_hidl_cb(Result::INVALID_OPERATION, -1 /* reportToken */);
|
||||
} else if (sensorHandle == -1 && rate != RateLevel::STOP) {
|
||||
_hidl_cb(Result::BAD_VALUE, -1 /* reportToken */);
|
||||
} else {
|
||||
// -1 denotes all sensors should be disabled
|
||||
if (sensorHandle != -1) {
|
||||
sensorHandle = clearSubHalIndex(sensorHandle);
|
||||
}
|
||||
mDirectChannelSubHal->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
|
||||
}
|
||||
return Return<void>();
|
||||
}
|
||||
|
||||
Return<void> HalProxy::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /*args*/) {
|
||||
if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
|
||||
ALOGE("%s: missing fd for writing", __FUNCTION__);
|
||||
return Void();
|
||||
}
|
||||
|
||||
android::base::borrowed_fd writeFd = dup(fd->data[0]);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "===HalProxy===" << std::endl;
|
||||
stream << "Internal values:" << std::endl;
|
||||
stream << " Threads are running: " << (mThreadsRun.load() ? "true" : "false") << std::endl;
|
||||
int64_t now = getTimeNow();
|
||||
stream << " Wakelock timeout start time: " << msFromNs(now - mWakelockTimeoutStartTime)
|
||||
<< " ms ago" << std::endl;
|
||||
stream << " Wakelock timeout reset time: " << msFromNs(now - mWakelockTimeoutResetTime)
|
||||
<< " ms ago" << std::endl;
|
||||
// TODO(b/142969448): Add logging for history of wakelock acquisition per subhal.
|
||||
stream << " Wakelock ref count: " << mWakelockRefCount << std::endl;
|
||||
stream << " # of events on pending write writes queue: " << mSizePendingWriteEventsQueue
|
||||
<< std::endl;
|
||||
stream << " Most events seen on pending write events queue: "
|
||||
<< mMostEventsObservedPendingWriteEventsQueue << std::endl;
|
||||
if (!mPendingWriteEventsQueue.empty()) {
|
||||
stream << " Size of events list on front of pending writes queue: "
|
||||
<< mPendingWriteEventsQueue.front().first.size() << std::endl;
|
||||
}
|
||||
stream << " # of non-dynamic sensors across all subhals: " << mSensors.size() << std::endl;
|
||||
stream << " # of dynamic sensors across all subhals: " << mDynamicSensors.size() << std::endl;
|
||||
stream << "SubHals (" << mSubHalList.size() << "):" << std::endl;
|
||||
for (auto& subHal : mSubHalList) {
|
||||
stream << " Name: " << subHal->getName() << std::endl;
|
||||
stream << " Debug dump: " << std::endl;
|
||||
android::base::WriteStringToFd(stream.str(), writeFd);
|
||||
subHal->debug(fd, {});
|
||||
stream.str("");
|
||||
stream << std::endl;
|
||||
}
|
||||
android::base::WriteStringToFd(stream.str(), writeFd);
|
||||
return Return<void>();
|
||||
}
|
||||
|
||||
Return<void> HalProxy::onDynamicSensorsConnected(const hidl_vec<SensorInfo>& dynamicSensorsAdded,
|
||||
int32_t subHalIndex) {
|
||||
std::vector<SensorInfo> sensors;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDynamicSensorsMutex);
|
||||
for (SensorInfo sensor : dynamicSensorsAdded) {
|
||||
if (!subHalIndexIsClear(sensor.sensorHandle)) {
|
||||
ALOGE("Dynamic sensor added %s had sensorHandle with first byte not 0.",
|
||||
sensor.name.c_str());
|
||||
} else {
|
||||
sensor.sensorHandle = setSubHalIndex(sensor.sensorHandle, subHalIndex);
|
||||
mDynamicSensors[sensor.sensorHandle] = sensor;
|
||||
sensors.push_back(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
mDynamicSensorsCallback->onDynamicSensorsConnected(sensors);
|
||||
return Return<void>();
|
||||
}
|
||||
|
||||
Return<void> HalProxy::onDynamicSensorsDisconnected(
|
||||
const hidl_vec<int32_t>& dynamicSensorHandlesRemoved, int32_t subHalIndex) {
|
||||
// TODO(b/143302327): Block this call until all pending events are flushed from queue
|
||||
std::vector<int32_t> sensorHandles;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mDynamicSensorsMutex);
|
||||
for (int32_t sensorHandle : dynamicSensorHandlesRemoved) {
|
||||
if (!subHalIndexIsClear(sensorHandle)) {
|
||||
ALOGE("Dynamic sensorHandle removed had first byte not 0.");
|
||||
} else {
|
||||
sensorHandle = setSubHalIndex(sensorHandle, subHalIndex);
|
||||
if (mDynamicSensors.find(sensorHandle) != mDynamicSensors.end()) {
|
||||
mDynamicSensors.erase(sensorHandle);
|
||||
sensorHandles.push_back(sensorHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDynamicSensorsCallback->onDynamicSensorsDisconnected(sensorHandles);
|
||||
return Return<void>();
|
||||
}
|
||||
|
||||
void HalProxy::initializeSubHalListFromConfigFile(const char* configFileName) {
|
||||
std::ifstream subHalConfigStream(configFileName);
|
||||
if (!subHalConfigStream) {
|
||||
ALOGE("Failed to load subHal config file: %s", configFileName);
|
||||
} else {
|
||||
std::string subHalLibraryFile;
|
||||
while (subHalConfigStream >> subHalLibraryFile) {
|
||||
void* handle = getHandleForSubHalSharedObject(subHalLibraryFile);
|
||||
if (handle == nullptr) {
|
||||
ALOGE("dlopen failed for library: %s", subHalLibraryFile.c_str());
|
||||
} else {
|
||||
SensorsHalGetSubHalFunc* sensorsHalGetSubHalPtr =
|
||||
(SensorsHalGetSubHalFunc*)dlsym(handle, "sensorsHalGetSubHal");
|
||||
if (sensorsHalGetSubHalPtr != nullptr) {
|
||||
std::function<SensorsHalGetSubHalFunc> sensorsHalGetSubHal =
|
||||
*sensorsHalGetSubHalPtr;
|
||||
uint32_t version;
|
||||
ISensorsSubHalV2_0* subHal = sensorsHalGetSubHal(&version);
|
||||
if (version != SUB_HAL_2_0_VERSION) {
|
||||
ALOGE("SubHal version was not 2.0 for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
} else {
|
||||
ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
|
||||
mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
|
||||
}
|
||||
} else {
|
||||
SensorsHalGetSubHalV2_1Func* getSubHalV2_1Ptr =
|
||||
(SensorsHalGetSubHalV2_1Func*)dlsym(handle, "sensorsHalGetSubHal_2_1");
|
||||
|
||||
if (getSubHalV2_1Ptr == nullptr) {
|
||||
ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
} else {
|
||||
std::function<SensorsHalGetSubHalV2_1Func> sensorsHalGetSubHal_2_1 =
|
||||
*getSubHalV2_1Ptr;
|
||||
uint32_t version;
|
||||
ISensorsSubHalV2_1* subHal = sensorsHalGetSubHal_2_1(&version);
|
||||
if (version != SUB_HAL_2_1_VERSION) {
|
||||
ALOGE("SubHal version was not 2.1 for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
} else {
|
||||
ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
|
||||
mSubHalList.push_back(std::make_unique<SubHalWrapperV2_1>(subHal));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::initializeSensorList() {
|
||||
for (size_t subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
|
||||
auto result = mSubHalList[subHalIndex]->getSensorsList([&](const auto& list) {
|
||||
for (SensorInfo sensor : list) {
|
||||
if (!subHalIndexIsClear(sensor.sensorHandle)) {
|
||||
ALOGE("SubHal sensorHandle's first byte was not 0");
|
||||
} else {
|
||||
ALOGV("Loaded sensor: %s", sensor.name.c_str());
|
||||
sensor.sensorHandle = setSubHalIndex(sensor.sensorHandle, subHalIndex);
|
||||
setDirectChannelFlags(&sensor, mSubHalList[subHalIndex]);
|
||||
if (sensor.typeAsString == "qti.sensor.wise_light") {
|
||||
sensor.type = SensorType::LIGHT;
|
||||
sensor.typeAsString = "";
|
||||
}
|
||||
mSensors[sensor.sensorHandle] = sensor;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!result.isOk()) {
|
||||
ALOGE("getSensorsList call failed for SubHal: %s",
|
||||
mSubHalList[subHalIndex]->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* HalProxy::getHandleForSubHalSharedObject(const std::string& filename) {
|
||||
static const std::string kSubHalShareObjectLocations[] = {
|
||||
"", // Default locations will be searched
|
||||
#ifdef __LP64__
|
||||
"/vendor/lib64/hw/", "/odm/lib64/hw/"
|
||||
#else
|
||||
"/vendor/lib/hw/", "/odm/lib/hw/"
|
||||
#endif
|
||||
};
|
||||
|
||||
for (const std::string& dir : kSubHalShareObjectLocations) {
|
||||
void* handle = dlopen((dir + filename).c_str(), RTLD_NOW);
|
||||
if (handle != nullptr) {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HalProxy::init() {
|
||||
initializeSensorList();
|
||||
}
|
||||
|
||||
void HalProxy::stopThreads() {
|
||||
mThreadsRun.store(false);
|
||||
if (mEventQueueFlag != nullptr && mEventQueue != nullptr) {
|
||||
size_t numToRead = mEventQueue->availableToRead();
|
||||
std::vector<Event> events(numToRead);
|
||||
mEventQueue->read(events.data(), numToRead);
|
||||
mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ));
|
||||
}
|
||||
if (mWakelockQueueFlag != nullptr && mWakeLockQueue != nullptr) {
|
||||
uint32_t kZero = 0;
|
||||
mWakeLockQueue->write(&kZero);
|
||||
mWakelockQueueFlag->wake(static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN));
|
||||
}
|
||||
mWakelockCV.notify_one();
|
||||
mEventQueueWriteCV.notify_one();
|
||||
if (mPendingWritesThread.joinable()) {
|
||||
mPendingWritesThread.join();
|
||||
}
|
||||
if (mWakelockThread.joinable()) {
|
||||
mWakelockThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::disableAllSensors() {
|
||||
for (const auto& sensorEntry : mSensors) {
|
||||
int32_t sensorHandle = sensorEntry.first;
|
||||
activate(sensorHandle, false /* enabled */);
|
||||
}
|
||||
std::lock_guard<std::mutex> dynamicSensorsLock(mDynamicSensorsMutex);
|
||||
for (const auto& sensorEntry : mDynamicSensors) {
|
||||
int32_t sensorHandle = sensorEntry.first;
|
||||
activate(sensorHandle, false /* enabled */);
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::startPendingWritesThread(HalProxy* halProxy) {
|
||||
halProxy->handlePendingWrites();
|
||||
}
|
||||
|
||||
void HalProxy::handlePendingWrites() {
|
||||
// TODO(b/143302327): Find a way to optimize locking strategy maybe using two mutexes instead of
|
||||
// one.
|
||||
std::unique_lock<std::mutex> lock(mEventQueueWriteMutex);
|
||||
while (mThreadsRun.load()) {
|
||||
mEventQueueWriteCV.wait(
|
||||
lock, [&] { return !mPendingWriteEventsQueue.empty() || !mThreadsRun.load(); });
|
||||
if (mThreadsRun.load()) {
|
||||
std::vector<Event>& pendingWriteEvents = mPendingWriteEventsQueue.front().first;
|
||||
size_t numWakeupEvents = mPendingWriteEventsQueue.front().second;
|
||||
size_t eventQueueSize = mEventQueue->getQuantumCount();
|
||||
size_t numToWrite = std::min(pendingWriteEvents.size(), eventQueueSize);
|
||||
lock.unlock();
|
||||
if (!mEventQueue->writeBlocking(
|
||||
pendingWriteEvents.data(), numToWrite,
|
||||
static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ),
|
||||
static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS),
|
||||
kPendingWriteTimeoutNs, mEventQueueFlag)) {
|
||||
ALOGE("Dropping %zu events after blockingWrite failed.", numToWrite);
|
||||
if (numWakeupEvents > 0) {
|
||||
if (pendingWriteEvents.size() > eventQueueSize) {
|
||||
decrementRefCountAndMaybeReleaseWakelock(
|
||||
countNumWakeupEvents(pendingWriteEvents, eventQueueSize));
|
||||
} else {
|
||||
decrementRefCountAndMaybeReleaseWakelock(numWakeupEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
lock.lock();
|
||||
mSizePendingWriteEventsQueue -= numToWrite;
|
||||
if (pendingWriteEvents.size() > eventQueueSize) {
|
||||
// TODO(b/143302327): Check if this erase operation is too inefficient. It will copy
|
||||
// all the events ahead of it down to fill gap off array at front after the erase.
|
||||
pendingWriteEvents.erase(pendingWriteEvents.begin(),
|
||||
pendingWriteEvents.begin() + eventQueueSize);
|
||||
} else {
|
||||
mPendingWriteEventsQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::startWakelockThread(HalProxy* halProxy) {
|
||||
halProxy->handleWakelocks();
|
||||
}
|
||||
|
||||
void HalProxy::handleWakelocks() {
|
||||
std::unique_lock<std::recursive_mutex> lock(mWakelockMutex);
|
||||
while (mThreadsRun.load()) {
|
||||
mWakelockCV.wait(lock, [&] { return mWakelockRefCount > 0 || !mThreadsRun.load(); });
|
||||
if (mThreadsRun.load()) {
|
||||
int64_t timeLeft;
|
||||
if (sharedWakelockDidTimeout(&timeLeft)) {
|
||||
resetSharedWakelock();
|
||||
} else {
|
||||
uint32_t numWakeLocksProcessed;
|
||||
lock.unlock();
|
||||
bool success = mWakeLockQueue->readBlocking(
|
||||
&numWakeLocksProcessed, 1, 0,
|
||||
static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN), timeLeft);
|
||||
lock.lock();
|
||||
if (success) {
|
||||
decrementRefCountAndMaybeReleaseWakelock(
|
||||
static_cast<size_t>(numWakeLocksProcessed));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resetSharedWakelock();
|
||||
}
|
||||
|
||||
bool HalProxy::sharedWakelockDidTimeout(int64_t* timeLeft) {
|
||||
bool didTimeout;
|
||||
int64_t duration = getTimeNow() - mWakelockTimeoutStartTime;
|
||||
if (duration > kWakelockTimeoutNs) {
|
||||
didTimeout = true;
|
||||
} else {
|
||||
didTimeout = false;
|
||||
*timeLeft = kWakelockTimeoutNs - duration;
|
||||
}
|
||||
return didTimeout;
|
||||
}
|
||||
|
||||
void HalProxy::resetSharedWakelock() {
|
||||
std::lock_guard<std::recursive_mutex> lockGuard(mWakelockMutex);
|
||||
decrementRefCountAndMaybeReleaseWakelock(mWakelockRefCount);
|
||||
mWakelockTimeoutResetTime = getTimeNow();
|
||||
}
|
||||
|
||||
void HalProxy::postEventsToMessageQueue(const std::vector<Event>& events, size_t numWakeupEvents,
|
||||
V2_0::implementation::ScopedWakelock wakelock) {
|
||||
size_t numToWrite = 0;
|
||||
std::lock_guard<std::mutex> lock(mEventQueueWriteMutex);
|
||||
if (wakelock.isLocked()) {
|
||||
incrementRefCountAndMaybeAcquireWakelock(numWakeupEvents);
|
||||
}
|
||||
if (mPendingWriteEventsQueue.empty()) {
|
||||
numToWrite = std::min(events.size(), mEventQueue->availableToWrite());
|
||||
if (numToWrite > 0) {
|
||||
if (mEventQueue->write(events.data(), numToWrite)) {
|
||||
// TODO(b/143302327): While loop if mEventQueue->avaiableToWrite > 0 to possibly fit
|
||||
// in more writes immediately
|
||||
mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS));
|
||||
} else {
|
||||
numToWrite = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t numLeft = events.size() - numToWrite;
|
||||
if (numToWrite < events.size() &&
|
||||
mSizePendingWriteEventsQueue + numLeft <= kMaxSizePendingWriteEventsQueue) {
|
||||
std::vector<Event> eventsLeft(events.begin() + numToWrite, events.end());
|
||||
mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents});
|
||||
mSizePendingWriteEventsQueue += numLeft;
|
||||
mMostEventsObservedPendingWriteEventsQueue =
|
||||
std::max(mMostEventsObservedPendingWriteEventsQueue, mSizePendingWriteEventsQueue);
|
||||
mEventQueueWriteCV.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool HalProxy::incrementRefCountAndMaybeAcquireWakelock(size_t delta,
|
||||
int64_t* timeoutStart /* = nullptr */) {
|
||||
if (!mThreadsRun.load()) return false;
|
||||
std::lock_guard<std::recursive_mutex> lockGuard(mWakelockMutex);
|
||||
if (mWakelockRefCount == 0) {
|
||||
acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakelockName);
|
||||
mWakelockCV.notify_one();
|
||||
}
|
||||
mWakelockTimeoutStartTime = getTimeNow();
|
||||
mWakelockRefCount += delta;
|
||||
if (timeoutStart != nullptr) {
|
||||
*timeoutStart = mWakelockTimeoutStartTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HalProxy::decrementRefCountAndMaybeReleaseWakelock(size_t delta,
|
||||
int64_t timeoutStart /* = -1 */) {
|
||||
if (!mThreadsRun.load()) return;
|
||||
std::lock_guard<std::recursive_mutex> lockGuard(mWakelockMutex);
|
||||
if (delta > mWakelockRefCount) {
|
||||
ALOGE("Decrementing wakelock ref count by %zu when count is %zu",
|
||||
delta, mWakelockRefCount);
|
||||
}
|
||||
if (timeoutStart == -1) timeoutStart = mWakelockTimeoutResetTime;
|
||||
if (mWakelockRefCount == 0 || timeoutStart < mWakelockTimeoutResetTime) return;
|
||||
mWakelockRefCount -= std::min(mWakelockRefCount, delta);
|
||||
if (mWakelockRefCount == 0) {
|
||||
release_wake_lock(kWakelockName);
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo,
|
||||
std::shared_ptr<ISubHalWrapperBase> subHal) {
|
||||
bool sensorSupportsDirectChannel =
|
||||
(sensorInfo->flags & (V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
|
||||
V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL)) != 0;
|
||||
if (mDirectChannelSubHal == nullptr && sensorSupportsDirectChannel) {
|
||||
mDirectChannelSubHal = subHal;
|
||||
} else if (mDirectChannelSubHal != nullptr && subHal != mDirectChannelSubHal) {
|
||||
// disable direct channel capability for sensors in subHals that are not
|
||||
// the only one we will enable
|
||||
sensorInfo->flags &= ~(V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
|
||||
V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ISubHalWrapperBase> HalProxy::getSubHalForSensorHandle(int32_t sensorHandle) {
|
||||
return mSubHalList[extractSubHalIndex(sensorHandle)];
|
||||
}
|
||||
|
||||
bool HalProxy::isSubHalIndexValid(int32_t sensorHandle) {
|
||||
return extractSubHalIndex(sensorHandle) < mSubHalList.size();
|
||||
}
|
||||
|
||||
size_t HalProxy::countNumWakeupEvents(const std::vector<Event>& events, size_t n) {
|
||||
size_t numWakeupEvents = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
int32_t sensorHandle = events[i].sensorHandle;
|
||||
if (mSensors[sensorHandle].flags & static_cast<uint32_t>(V1_0::SensorFlagBits::WAKE_UP)) {
|
||||
numWakeupEvents++;
|
||||
}
|
||||
}
|
||||
return numWakeupEvents;
|
||||
}
|
||||
|
||||
int32_t HalProxy::clearSubHalIndex(int32_t sensorHandle) {
|
||||
return sensorHandle & (~kSensorHandleSubHalIndexMask);
|
||||
}
|
||||
|
||||
bool HalProxy::subHalIndexIsClear(int32_t sensorHandle) {
|
||||
return (sensorHandle & kSensorHandleSubHalIndexMask) == 0;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_1
|
||||
} // namespace sensors
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#include "HalProxyCallback.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace sensors {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
static constexpr int32_t kBitsAfterSubHalIndex = 24;
|
||||
|
||||
/**
|
||||
* Set the subhal index as first byte of sensor handle and return this modified version.
|
||||
*
|
||||
* @param sensorHandle The sensor handle to modify.
|
||||
* @param subHalIndex The index in the hal proxy of the sub hal this sensor belongs to.
|
||||
*
|
||||
* @return The modified sensor handle.
|
||||
*/
|
||||
int32_t setSubHalIndex(int32_t sensorHandle, size_t subHalIndex) {
|
||||
return sensorHandle | (static_cast<int32_t>(subHalIndex) << kBitsAfterSubHalIndex);
|
||||
}
|
||||
|
||||
void HalProxyCallbackBase::postEvents(const std::vector<V2_1::Event>& events,
|
||||
ScopedWakelock wakelock) {
|
||||
if (events.empty() || !mCallback->areThreadsRunning()) return;
|
||||
size_t numWakeupEvents;
|
||||
std::vector<V2_1::Event> processedEvents = processEvents(events, &numWakeupEvents);
|
||||
if (numWakeupEvents > 0) {
|
||||
ALOG_ASSERT(wakelock.isLocked(),
|
||||
"Wakeup events posted while wakelock unlocked for subhal"
|
||||
" w/ index %" PRId32 ".",
|
||||
mSubHalIndex);
|
||||
} else {
|
||||
ALOG_ASSERT(!wakelock.isLocked(),
|
||||
"No Wakeup events posted but wakelock locked for subhal"
|
||||
" w/ index %" PRId32 ".",
|
||||
mSubHalIndex);
|
||||
}
|
||||
mCallback->postEventsToMessageQueue(processedEvents, numWakeupEvents, std::move(wakelock));
|
||||
}
|
||||
|
||||
ScopedWakelock HalProxyCallbackBase::createScopedWakelock(bool lock) {
|
||||
ScopedWakelock wakelock(mRefCounter, lock);
|
||||
return wakelock;
|
||||
}
|
||||
|
||||
std::vector<V2_1::Event> HalProxyCallbackBase::processEvents(const std::vector<V2_1::Event>& events,
|
||||
size_t* numWakeupEvents) const {
|
||||
*numWakeupEvents = 0;
|
||||
std::vector<V2_1::Event> eventsOut;
|
||||
for (V2_1::Event event : events) {
|
||||
event.sensorHandle = setSubHalIndex(event.sensorHandle, mSubHalIndex);
|
||||
eventsOut.push_back(event);
|
||||
const V2_1::SensorInfo& sensor = mCallback->getSensorInfo(event.sensorHandle);
|
||||
if ((sensor.flags & V1_0::SensorFlagBits::WAKE_UP) != 0) {
|
||||
(*numWakeupEvents)++;
|
||||
}
|
||||
}
|
||||
return eventsOut;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace sensors
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -1,11 +0,0 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.sensors</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>2.0</version>
|
||||
<interface>
|
||||
<name>ISensors</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
@@ -1,7 +0,0 @@
|
||||
service vendor.sensors-hal-2-0-multihal /vendor/bin/hw/android.hardware.sensors@2.0-service.multihal
|
||||
class hal
|
||||
user system
|
||||
group system wakelock context_hub input
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
capabilities BLOCK_SUSPEND
|
||||
rlimit rtprio 10 10
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#include <android/hardware/sensors/2.0/ISensors.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <log/log.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include "HalProxy.h"
|
||||
|
||||
using android::hardware::configureRpcThreadpool;
|
||||
using android::hardware::joinRpcThreadpool;
|
||||
using android::hardware::sensors::V2_0::ISensors;
|
||||
using android::hardware::sensors::V2_1::implementation::HalProxyV2_0;
|
||||
|
||||
int main(int /* argc */, char** /* argv */) {
|
||||
configureRpcThreadpool(1, true);
|
||||
|
||||
android::sp<ISensors> halProxy = new HalProxyV2_0();
|
||||
if (halProxy->registerAsService() != ::android::OK) {
|
||||
ALOGE("Failed to register Sensors HAL instance");
|
||||
return -1;
|
||||
}
|
||||
|
||||
joinRpcThreadpool();
|
||||
return 1; // joinRpcThreadpool shouldn't exit
|
||||
}
|
||||
6
sepolicy/vendor/devicesettings_app.te
vendored
Normal file
6
sepolicy/vendor/devicesettings_app.te
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
type devicesettings_app, domain;
|
||||
app_domain(devicesettings_app)
|
||||
|
||||
# Allow charging bypass to overwrite
|
||||
allow devicesettings_app sysfs_battery_writable:dir r_dir_perms;
|
||||
allow devicesettings_app sysfs_battery_writable:file { open read write getattr ioctl };
|
||||
3
sepolicy/vendor/file.te
vendored
3
sepolicy/vendor/file.te
vendored
@@ -13,6 +13,9 @@ type proc_oppo_fp, fs_type, proc_type;
|
||||
# Camera
|
||||
type vendor_persist_camera_file, file_type;
|
||||
|
||||
# Bypass Charging
|
||||
type sysfs_battery_writable, sysfs_type, fs_type;
|
||||
|
||||
# Devinfo
|
||||
type proc_devinfo, fs_type, proc_type;
|
||||
|
||||
|
||||
6
sepolicy/vendor/file_contexts
vendored
6
sepolicy/vendor/file_contexts
vendored
@@ -16,6 +16,12 @@
|
||||
# Camera
|
||||
/mnt/vendor/persist/camera(/.*)? u:object_r:vendor_persist_camera_file:s0
|
||||
|
||||
# Cust
|
||||
/dev/block/platform/soc/4744000.sdhci/by-name/cust u:object_r:vendor_custom_block_device:s0
|
||||
|
||||
# Charging Bypass
|
||||
/sys/class/power_supply/battery/input_suspend u:object_r:sysfs_battery_writable:s0
|
||||
|
||||
# Oppo Partitions
|
||||
/dev/block/platform/soc/1d84000.ufshc/by-name/oppodycnvbk u:object_r:vendor_modem_efs_partition_device:s0
|
||||
/dev/block/platform/soc/1d84000.ufshc/by-name/oppostanvbk u:object_r:vendor_modem_efs_partition_device:s0
|
||||
|
||||
1
sepolicy/vendor/hal_power_default.te
vendored
1
sepolicy/vendor/hal_power_default.te
vendored
@@ -1,4 +1,5 @@
|
||||
allow hal_power_default proc_touchpanel:dir search;
|
||||
allow hal_power_default proc_touchpanel:file rw_file_perms;
|
||||
allow init sysfs_battery_supply:file rw_file_perms;
|
||||
|
||||
get_prop(hal_power_default, vendor_mpctl_prop);
|
||||
|
||||
6
sepolicy/vendor/system_app.te
vendored
6
sepolicy/vendor/system_app.te
vendored
@@ -6,5 +6,9 @@ r_dir_file(system_app, proc_touchpanel);
|
||||
|
||||
r_dir_file(system_app, vendor_sysfs_usb_supply);
|
||||
allow system_app vendor_sysfs_usb_supply:file rw_file_perms;
|
||||
allow system_app vendor_sysfs_battery_supply:dir search;
|
||||
allow system_app vendor_sysfs_battery_supply:file read;
|
||||
allow system_app vendor_sysfs_battery_supply:file getattr;
|
||||
|
||||
|
||||
# Allow Settings app to control input_suspend (bypass charging)
|
||||
allow system_app vendor_sysfs_battery_supply:file rw_file_perms;
|
||||
|
||||
4
sepolicy/vendor/system_server.te
vendored
Normal file
4
sepolicy/vendor/system_server.te
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
allow system_server proc_devinfo:dir search;
|
||||
allow system_server proc_oppo_rf:dir search;
|
||||
allow system_server proc_oppoversion:dir search;
|
||||
allow system_server proc_touchpanel:dir search;
|
||||
4
sepolicy/vendor/vendor_qti_init_shell.te
vendored
4
sepolicy/vendor/vendor_qti_init_shell.te
vendored
@@ -6,3 +6,7 @@ get_prop(vendor_qti_init_shell, oppo_debug_prop)
|
||||
allow vendor_qti_init_shell sysfs:file write;
|
||||
|
||||
allow vendor_qti_init_shell proc_page_cluster:file { open };
|
||||
|
||||
|
||||
# To set powerhal init property
|
||||
set_prop(vendor_qti_init_shell, vendor_power_prop)
|
||||
@@ -21,11 +21,13 @@ ro.camera.attr.detect.enable=1
|
||||
ro.camera.notify_nfc=1
|
||||
ro.camera.temperature.limit=470
|
||||
persist.vendor.camera.light_ae.enable=1
|
||||
persist.camera.eis.enable=1
|
||||
persist.camera.is_mode=4
|
||||
persist.vendor.camera.oissr.debug=1
|
||||
vendor.debug.camera.eisv2enable=1
|
||||
vendor.debug.camera.eisv3enable=1
|
||||
# Allow privileged camera apps
|
||||
persist.vendor.camera.privapp.list=com.android.camera,com.google.camera,org.lineageos.aperture
|
||||
# Allow aux cameras
|
||||
vendor.camera.aux.packagelist=com.android.camera,com.google.camera,org.lineageos.aperture
|
||||
|
||||
# CNE
|
||||
persist.vendor.cne.feature=1
|
||||
|
||||
@@ -97,6 +97,8 @@ vendor.qcom.bluetooth.soc=cherokee
|
||||
|
||||
# Camera
|
||||
camera.disable_zsl_mode=1
|
||||
persist.vendor.camera.eis.enable=1
|
||||
persist.vendor.camera.is_type=2
|
||||
|
||||
# Crypto
|
||||
ro.crypto.allow_encrypt_override=true
|
||||
@@ -222,3 +224,7 @@ sys.fflag.override.settings_seamless_transfer=true
|
||||
|
||||
# default refresh rate threshold
|
||||
debug.sf.frame_rate_multiple_threshold=0
|
||||
|
||||
# Fingerprint
|
||||
ro.vendor.fingerprint.type=side
|
||||
ro.vendor.fingerprint.supports_gestures=true
|
||||
|
||||
Reference in New Issue
Block a user