diff --git a/Android.bp b/Android.bp index 4ca8db0a..c35e6ec3 100644 --- a/Android.bp +++ b/Android.bp @@ -11,6 +11,7 @@ soong_namespace { "hardware/qcom-caf/common/libqti-perfd-client", "hardware/xiaomi", "vendor/qcom/opensource/commonsys-intf/display", + "vendor/xiaomi/garnet", ], } diff --git a/BoardConfig.mk b/BoardConfig.mk index fe9122ae..f7b2fa71 100644 --- a/BoardConfig.mk +++ b/BoardConfig.mk @@ -4,10 +4,15 @@ # SPDX-License-Identifier: Apache-2.0 # + + DEVICE_PATH := device/xiaomi/garnet BUILD_BROKEN_DUP_RULES := true +BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES := true + + # A/B AB_OTA_PARTITIONS := \ boot \ @@ -56,6 +61,7 @@ TARGET_NO_BOOTLOADER := true # Display TARGET_SCREEN_DENSITY := 480 +TARGET_USES_VULKAN := true # Filesystem TARGET_FS_CONFIG_GEN := $(DEVICE_PATH)/configs/config.fs @@ -63,9 +69,15 @@ TARGET_FS_CONFIG_GEN := $(DEVICE_PATH)/configs/config.fs # Hardware BOARD_USES_QCOM_HARDWARE := true +# Global LTO +TARGET_GLOBAL_LTO := thin +TARGET_GLOBAL_OPTIMIZATION := O3 +TARGET_GLOBAL_THINLTO := true + # HIDL DEVICE_MATRIX_FILE := hardware/qcom-caf/common/compatibility_matrix.xml DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE += \ + $(DEVICE_PATH)/configs/hidl/device_framework_matrix.xml \ hardware/qcom-caf/common/vendor_framework_compatibility_matrix.xml \ hardware/xiaomi/vintf/xiaomi_framework_compatibility_matrix.xml @@ -193,6 +205,7 @@ include device/lineage/sepolicy/libperfmgr/sepolicy.mk include device/qcom/sepolicy_vndr/SEPolicy.mk BOARD_VENDOR_SEPOLICY_DIRS += $(DEVICE_PATH)/sepolicy/vendor +SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += $(DEVICE_PATH)/sepolicy/private # System properties TARGET_ODM_PROP += $(DEVICE_PATH)/props/odm.prop @@ -201,7 +214,7 @@ TARGET_SYSTEM_EXT_PROP += $(DEVICE_PATH)/props/system_ext.prop TARGET_VENDOR_PROP += $(DEVICE_PATH)/props/vendor.prop # Vendor security patch -VENDOR_SECURITY_PATCH := 2025-09-01 +VENDOR_SECURITY_PATCH := $(PLATFORM_SECURITY_PATCH) # Verified Boot BOARD_AVB_ENABLE := true @@ -237,3 +250,6 @@ WPA_SUPPLICANT_VERSION := VER_0_8_X # Vendor include vendor/xiaomi/garnet/BoardConfigVendor.mk + +# Vendor MiuiCamera +-include device/xiaomi/miuicamera-garnet/BoardConfig.mk diff --git a/Euicc/Android.bp b/Euicc/Android.bp deleted file mode 100644 index fff013d3..00000000 --- a/Euicc/Android.bp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (C) 2023 The LineageOS Project -// -// SPDX-License-Identifier: Apache-2.0 -// - -android_app { - name: "XiaomiEuicc", - - srcs: ["src/**/*.kt"], - resource_dirs: ["res"], - - certificate: "platform", - platform_apis: true, - privileged: true, - system_ext_specific: true, - - optimize: { - proguard_flags_files: ["proguard.flags"], - }, - required: [ - "org.lineageos.euicc.xml", - "hidden-api-whitelist-org.lineageos.euicc.xml", - ], -} - -prebuilt_etc { - sub_dir: "permissions", - name: "org.lineageos.euicc.xml", - filename: "org.lineageos.euicc.xml", - src: "org.lineageos.euicc.xml", - system_ext_specific: true, -} - -prebuilt_etc { - sub_dir: "sysconfig", - name: "hidden-api-whitelist-org.lineageos.euicc.xml", - filename: "hidden-api-whitelist-org.lineageos.euicc.xml", - src: "hidden-api-whitelist-org.lineageos.euicc.xml", - system_ext_specific: true, -} diff --git a/Euicc/AndroidManifest.xml b/Euicc/AndroidManifest.xml deleted file mode 100644 index 947e9550..00000000 --- a/Euicc/AndroidManifest.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Euicc/hidden-api-whitelist-org.lineageos.euicc.xml b/Euicc/hidden-api-whitelist-org.lineageos.euicc.xml deleted file mode 100644 index b3931ef9..00000000 --- a/Euicc/hidden-api-whitelist-org.lineageos.euicc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/Euicc/org.lineageos.euicc.xml b/Euicc/org.lineageos.euicc.xml deleted file mode 100644 index 3e656060..00000000 --- a/Euicc/org.lineageos.euicc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Euicc/proguard.flags b/Euicc/proguard.flags deleted file mode 100644 index 2632b6c3..00000000 --- a/Euicc/proguard.flags +++ /dev/null @@ -1,3 +0,0 @@ --keep class org.lineageos.euicc.* { - *; -} diff --git a/Euicc/res/raw/esim_intro_illustration_lottie.json b/Euicc/res/raw/esim_intro_illustration_lottie.json deleted file mode 100644 index 458af8c6..00000000 --- a/Euicc/res/raw/esim_intro_illustration_lottie.json +++ /dev/null @@ -1 +0,0 @@ -{"nm":"Comp 1","ddd":0,"h":300,"w":412,"meta":{"g":"@lottiefiles/toolkit-js 0.26.1"},"layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"rc","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":[3.648070510939896,37.331014240589184],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[214.06544494628906,135.26850128173828],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[10.213248145973182,9.645845471196907],"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[231.533120364933,61.76888407228705],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2},{"ty":3,"nm":"deleted","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[50,50],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[270.5,147.5],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":3},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":200,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[160,160],"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":4,"ix":2},"c":{"a":0,"k":[0.9098,0.9176,0.9294],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4,"parent":3},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[160,160],"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":4,"ix":2},"c":{"a":0,"k":[0.9176,0.2627,0.2078],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10],"t":0},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50],"t":100},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":148},{"s":[100],"t":200}],"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":52},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[12.44],"t":100},{"s":[90],"t":200}],"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[-18],"t":0},{"s":[378],"t":200}],"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":5,"parent":3},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[264.392,157.857],[276.516,157.857]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.5294,0.6824,0.9882],"t":75},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":97},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":175},{"s":[0.5294,0.6824,0.9882],"t":200}],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":6},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.918,-2.459],[-0.001,2.459],[-4.919,-2.459]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.5294,0.6824,0.9882],"t":75},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":97},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":175},{"s":[0.5294,0.6824,0.9882],"t":200}],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4540100097656,151.4750061035156],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":7},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[270.453,141.169],[270.453,153.934]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.5294,0.6824,0.9882],"t":75},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":97},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[1,1,1],"t":175},{"s":[0.5294,0.6824,0.9882],"t":200}],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":8},{"ty":4,"nm":"g","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.633],[0,0],[-1.32,0],[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0.447,-0.448],[0,0],[0,-0.633]],"o":[[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0,-1.319],[0,0],[-0.634,0],[0,0],[-0.447,0.447],[0,0]],"v":[[-12.082,-5.689],[-12.082,13.612],[-9.693,16],[9.692,16],[12.081,13.612],[12.081,-13.611],[9.692,-16],[-1.771,-16],[-3.46,-15.3],[-11.383,-7.377],[-12.082,-5.689]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.2039,0.6588,0.3255],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"s":[-3888],"t":200}],"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4540100097656,147.7409973144531],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":9},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.633],[0,0],[-1.32,0],[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0.447,-0.448],[0,0],[0,-0.633]],"o":[[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0,-1.319],[0,0],[-0.634,0],[0,0],[-0.447,0.447],[0,0]],"v":[[-12.082,-5.689],[-12.082,13.612],[-9.693,16],[9.692,16],[12.081,13.612],[12.081,-13.611],[9.692,-16],[-1.771,-16],[-3.46,-15.3],[-11.383,-7.377],[-12.082,-5.689]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4540100097656,147.7409973144531],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":10},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.633],[0,0],[-1.32,0],[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0.447,-0.448],[0,0],[0,-0.633]],"o":[[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0,-1.319],[0,0],[-0.634,0],[0,0],[-0.447,0.447],[0,0]],"v":[[-12.082,-5.689],[-12.082,13.612],[-9.693,16],[9.692,16],[12.081,13.612],[12.081,-13.611],[9.692,-16],[-1.771,-16],[-3.46,-15.3],[-11.383,-7.377],[-12.082,-5.689]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4540100097656,147.7409973144531],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":11},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[1.105,0],[0,0],[0,1.104],[0,0],[1.105,0]],"o":[[0,0],[1.105,0],[0,0],[0,-1.104],[0,0]],"v":[[-1,-6],[-1,6],[1,4],[1,-4],[-1,-6]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[327.4530029296875,80],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":12},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[1.105,0],[0,0],[0,1.104],[0,0],[1.105,0]],"o":[[0,0],[1.105,0],[0,0],[0,-1.104],[0,0]],"v":[[-1,-12],[-1,12],[1,10],[1,-10],[-1,-12]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[327.4530029296875,137.25],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":13},{"ty":4,"nm":"Group 3","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,4.963],[0,0],[-4.962,0],[0,0],[0,-4.962],[0,0],[4.963,0],[0,0]],"o":[[-4.962,0],[0,0],[0,-4.962],[0,0],[4.963,0],[0,0],[0,4.963],[0,0],[-4.962,0]],"v":[[-46,105],[-55,96],[-55,-96],[-46,-105],[46,-105],[55,-96],[55,96],[46,105],[-46,105]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.8549,0.8627,0.8784],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4530029296875,151.25],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":14},{"ty":4,"nm":"Group 5","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,3.584],[0,0],[-3.584,0],[0,0],[0,-3.584],[0,0],[3.584,0],[0,0]],"o":[[-3.584,0],[0,0],[0,-3.584],[0,0],[3.584,0],[0,0],[0,3.584],[0,0],[-3.584,0]],"v":[[-46,102.5],[-52.5,96],[-52.5,-96],[-46,-102.5],[46,-102.5],[52.5,-96],[52.5,96],[46,102.5],[-46,102.5]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[270.4530029296875,151.25],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":15},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-1.221,4.234],[1.221,4.234],[1.221,-4.233],[-1.221,-4.233],[-1.221,4.234]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9176,0.2627,0.2078],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[158.7989959716797,145.7510070800781],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":16},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-1.221,2.559],[1.221,2.559],[1.221,-2.559],[-1.221,-2.559],[-1.221,2.559]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9176,0.2627,0.2078],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[163.9530029296875,147.4259948730469],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":17},{"ty":4,"nm":"Group 3","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.221,-3.359],[-1.221,-3.359],[-1.221,3.359],[1.221,3.359],[1.221,-3.359]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9176,0.2627,0.2078],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[163.9539947509766,138.1580047607422],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":18},{"ty":4,"nm":"Group 4","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.221,-1.679],[-1.221,-1.679],[-1.221,1.68],[1.221,1.68],[1.221,-1.679]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9176,0.2627,0.2078],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[158.7989959716797,136.4779968261719],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":19},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.633],[0,0],[-1.319,0],[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0.448,-0.448],[0,0],[0,-0.633]],"o":[[0,0],[0,1.319],[0,0],[1.32,0],[0,0],[0,-1.319],[0,0],[-0.634,0],[0,0],[-0.448,0.448],[0,0]],"v":[[-12.081,-5.689],[-12.081,13.612],[-9.694,16],[9.692,16],[12.081,13.612],[12.081,-13.611],[9.692,-16],[-1.771,-16],[-3.46,-15.3],[-11.383,-7.378],[-12.081,-5.689]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[157.5780029296875,147.4299926757812],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":20},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-13.336,-13.336]],"o":[[13.336,-13.336],[0,0]],"v":[[-24.147,6.668],[24.147,6.668]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[109.693000793457,168.2310028076172],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":21},{"ty":4,"nm":"Group 3","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-18.387,0],[0,-18.387]],"o":[[0,-18.387],[18.387,0],[0,0]],"v":[[-33.292,16.646],[0.001,-16.646],[33.292,16.646]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[188.6020050048828,158.2530059814453],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":22},{"ty":4,"nm":"Group 4","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-14.73,0],[0,-14.729]],"o":[[0,-14.729],[14.73,0],[0,0]],"v":[[-26.671,13.335],[0,-13.335],[26.671,13.335]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[128.6380004882812,161.5639953613281],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":23},{"ty":4,"nm":"Group 5","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-13.336,13.336],[-13.336,-13.336],[13.336,-13.336]],"o":[[-13.336,-13.336],[13.336,-13.336],[13.336,13.336],[0,0]],"v":[[-24.147,30.815],[-24.147,-17.479],[24.147,-17.479],[24.147,30.815]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[158.947998046875,144.0839996337891],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":24},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":200,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[43,43],"t":0},{"s":[126,126],"t":200}],"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8],"t":0},{"s":[0],"t":200}],"ix":2},"c":{"a":0,"k":[0.5294,0.6824,0.9882],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[159,150],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":25}],"v":"5.7.5","fr":100,"op":200,"ip":0,"assets":[{"nm":"","id":"1","layers":[]},{"nm":"","id":"2","layers":[]}]} \ No newline at end of file diff --git a/Euicc/res/raw/sim_illustration_lottie_bottom.json b/Euicc/res/raw/sim_illustration_lottie_bottom.json deleted file mode 100644 index e71d7d2e..00000000 --- a/Euicc/res/raw/sim_illustration_lottie_bottom.json +++ /dev/null @@ -1 +0,0 @@ -{"nm":"Comp 1","ddd":0,"h":300,"w":412,"meta":{"g":"@lottiefiles/toolkit-js 0.26.1"},"layers":[{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[805.4626312255859,443.3493957519531],"ix":2},"s":{"a":0,"k":[4,4],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[243.865478515625,71.48397827148438],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":1},{"ty":4,"nm":"logo_1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[2,39],[10,14],[0,9],[9,9],[14,20],[29,0],[4,4],[84,6],[24,1.66666666666697],[-0.6666666666669698,60.33333333333303],[-1,60.66666666666697],[-81.33333333333303,0.6666666666669698],[-8,3],[0,614],[0,248.3333333333335],[-85,0],[-85,0],[0,-390],[0,-390],[75,0],[8,15],[0,0],[0,0]],"o":[[-5,-10],[-3,-43],[-9,-13],[0,-9],[-10,-9],[-27,-39],[-10,0],[-11,-11],[-24,-1.66666666666697],[0.6666666666669698,-60.33333333333303],[1,-60.66666666666697],[81.33333333333303,-0.6666666666669698],[134,-1],[11,-4],[0,-248.3333333333335],[85,0],[85,0],[0,390],[0,390],[-75,0],[-203,0],[0,0],[0,0],[-5,-10]],"v":[[572,2323],[558,2234],[536,2139],[520,2100],[503,2068],[460,2016],[322,1920],[296,1913],[72,1873],[0,1868],[2,1687],[5,1505],[249,1503],[507,1496],[520,745],[520,0],[775,0],[1030,0],[1030,1170],[1030,2340],[805,2340],[572,2323],[572,2323],[572,2323]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[12.5,-12.5],"ix":2},"sk":{"a":0,"k":7.016709604711e-15,"ix":2},"p":{"a":0,"k":[662.5,643.75],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2,"parent":1},{"ty":4,"nm":"logo_base","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[-3,1315],[-1.666666666666515,797.666666666667],[-798.3333333333339,0],[-798.3333333333339,0],[-1,-456],[-0.6666666666660603,-455.666666666667],[86.66666666666606,0],[86.66666666666697,0],[0,370],[0,370],[626.666666666667,0],[626.666666666667,0],[0,-626.666666666667],[0,-626.666666666667],[-368.333333333333,0],[-368.333333333333,0],[0,-88.33333333333303],[0,-88.33333333333303],[454.666666666667,0],[3,7],[0,0],[0,0]],"o":[[-1,-5],[1.666666666666515,-797.6666666666665],[798.333333333333,0],[798.333333333333,0],[1,456],[0.6666666666660603,455.6666666666665],[-86.66666666666606,0],[-86.66666666666697,0],[0,-370],[0,-370],[-626.666666666667,0],[-626.666666666667,0],[0,626.6666666666665],[0,626.666666666667],[368.333333333333,0],[368.333333333333,0],[0,88.33333333333303],[0,88.33333333333303],[-454.666666666667,0],[-901,0],[0,0],[0,0],[-1,-5]],"v":[[0.5980762113531455,4795],[2.598076211353145,2393],[7.598076211353145,0],[2402.598076211353,0],[4797.598076211353,0],[4800.598076211353,1368],[4802.598076211353,2735],[4542.598076211353,2735],[4282.598076211353,2735],[4282.598076211353,1625],[4282.598076211353,515],[2402.598076211353,515],[522.5980762113531,515],[522.5980762113531,2395],[522.5980762113531,4275],[1627.598076211353,4275],[2732.598076211353,4275],[2732.598076211353,4540],[2732.598076211353,4805],[1368.598076211353,4805],[0.5980762113531455,4795],[0.5980762113531455,4795],[0.5980762113531455,4795]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[12.5,-12.5],"ix":2},"sk":{"a":0,"k":7.016709604711e-15,"ix":2},"p":{"a":0,"k":[449.6752624511719,799.375],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":3,"parent":1},{"ty":4,"nm":"logo_plus","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[81.66666666666697,1.66666666666697],[1,147.333333333333],[10,0],[239,2],[145,0.6666666666669698],[0,86.66666666666697],[0,86.66666666666697],[-149,1],[-149.333333333333,0.6666666666669698],[-0.6666666666669698,147],[-1,147],[-83,-0.3333333333330302],[-5,-3],[0,-232],[0,-7],[-349,0],[-148.3333333333339,0],[0,-88.33333333333303],[0,-88.33333333333303],[147.3333333333339,-0.6666666666669698],[147.6666666666661,-1],[1,-145.333333333333],[10,-6],[135,3],[0,0],[0,0]],"o":[[-81.66666666666697,-1.66666666666697],[-1,-147.333333333333],[-2,-344],[-6,0],[-145,-0.6666666666669698],[0,-86.66666666666697],[0,-86.66666666666697],[149,-1],[149.333333333333,-0.6666666666669698],[0.6666666666669698,-147],[1,-147],[83,0.3333333333330302],[137,1],[5,3],[0,232],[0,9],[148.3333333333339,0],[0,88.33333333333303],[0,88.33333333333303],[-147.3333333333339,0.6666666666669698],[-147.6666666666661,1],[-1,145.333333333333],[-2,334],[-6,5],[0,0],[0,0],[-81.66666666666697,-1.66666666666697]],"v":[[1145,2302],[900,2297],[897,1855],[882,1412],[435,1409],[0,1407],[0,1147],[0,887],[447,884],[895,882],[897,441],[900,0],[1149,1],[1407,8],[1415,435],[1415,870],[1860,882],[2305,882],[2305,1147],[2305,1412],[1863,1414],[1420,1417],[1417,1853],[1402,2298],[1145,2302],[1145,2302],[1145,2302]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[12.5,-12.5],"ix":2},"sk":{"a":0,"k":7.016709604711e-15,"ix":2},"p":{"a":0,"k":[873.125,375.2499694824219],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4,"parent":1},{"ty":4,"nm":"button","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-17.5],[0,17.5]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.502,0.5255,0.5451],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[184.1,97],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":5},{"ty":4,"nm":"camera","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[11.94000244140625,11.5],"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.502,0.5255,0.5451],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[70,70],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[200.0299987792969,20.25],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":6},{"ty":0,"nm":"circle_outline (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":599},{"s":[0],"t":600}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"1","ind":7},{"ty":0,"nm":"circle 3 (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":602},{"s":[0],"t":603}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"2","ind":8},{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[130.549,498.039],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50,50],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":533},{"s":[50,50],"t":590}],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[226.417,218.701],"t":533,"ti":[-0.167,-2.833],"to":[0.167,2.833]},{"s":[227.417,235.701],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":9},{"ty":0,"nm":"blueline (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":132},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":133},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":596},{"s":[0],"t":597}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"3","ind":10,"parent":9},{"ty":0,"nm":"PhoneOutlines","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"6","ind":11},{"ty":0,"nm":".black","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"9","ind":12},{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[130.549,498.039],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50,50],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":533},{"s":[50,50],"t":590}],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[226.417,218.701],"t":533,"ti":[-0.167,-2.833],"to":[0.167,2.833]},{"s":[227.417,235.701],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":13},{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[31.043,31.992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,442.043],"t":217,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":333,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":433,"ti":[0,0],"to":[0,0]},{"s":[113.658,442.043],"t":533}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":14,"parent":13},{"ty":0,"nm":"sim 2 (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":399},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":400},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":532},{"s":[0],"t":533}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"11","ind":15,"parent":14},{"ty":0,"nm":"sim (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":299},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":300},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":399},{"s":[0],"t":400}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"13","ind":16},{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[130.549,498.039],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50,50],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":533},{"s":[50,50],"t":590}],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[226.417,218.701],"t":533,"ti":[-0.167,-2.833],"to":[0.167,2.833]},{"s":[227.417,235.701],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":17},{"ty":3,"nm":"deleted_deleted","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[31.043,31.992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,442.043],"t":217,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":333,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":433,"ti":[0,0],"to":[0,0]},{"s":[113.658,442.043],"t":533}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ind":18,"parent":17},{"ty":0,"nm":"support (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":192},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":193},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":546},{"s":[0],"t":547}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"14","ind":19,"parent":18},{"ty":0,"nm":"tool (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":302},{"s":[0],"t":303}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"16","ind":20},{"ty":0,"nm":".black (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":599},{"s":[0],"t":600}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"17","ind":21},{"ty":0,"nm":"line_P3 (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":-41},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":-40},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":119},{"s":[0],"t":120}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"18","ind":22},{"ty":4,"nm":"phone","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[4.405,0],[0,0],[0,4.405],[0,0],[-4.405,0],[0,0],[0,-4.405],[0,0]],"o":[[0,4.405],[0,0],[-4.405,0],[0,0],[0,-4.405],[0,0],[4.405,0],[0,0],[0,4.405]],"v":[[53.826,96.461],[45.839,104.45],[-46.029,104.45],[-54.017,96.461],[-54.017,-95.261],[-46.029,-103.25],[45.839,-103.25],[53.826,-95.261],[53.826,96.461]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,1.103],[0,0],[1.103,0],[0,0],[5.506,0],[0,0],[0,-5.506],[0,0],[-5.506,0],[0,0],[0,5.506],[0,0],[0,1.103],[0,0],[1.103,0],[0,0],[0,1.103]],"o":[[0,0],[0,-1.103],[0,0],[0,-5.506],[0,0],[-5.506,0],[0,0],[0,5.506],[0,0],[5.506,0],[0,0],[1.103,0],[0,0],[0,-1.103],[0,0],[1.103,0],[0,0]],"v":[[57.821,-43.337],[57.821,-51.325],[55.824,-53.322],[55.824,-95.261],[45.839,-105.247],[-46.029,-105.247],[-56.014,-95.261],[-56.014,96.461],[-46.029,106.447],[45.839,106.447],[55.824,96.461],[55.824,-1.397],[57.821,-3.394],[57.821,-23.365],[55.824,-25.363],[55.824,-41.339],[57.821,-43.337]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.502,0.5255,0.5451],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[241,113],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":23},{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[15.4,0],[0,0],[0,15.4],[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0]],"o":[[0,0],[-15.4,0],[0,0],[0,-15.4],[0,0],[15.4,0],[0,0],[0,15.4],[0,0]],"v":[[178,150],[-178,150],[-206,122],[-206,-122],[-178,-150],[178,-150],[206,-122],[206,122],[178,150]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[206,150],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":24}],"v":"5.7.5","fr":100,"op":633.3333333333333,"ip":0,"assets":[{"nm":"","id":"1","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10198.197998046875,10211.68099975586],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20,0],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":127},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":533},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":590},{"s":[20,0],"t":600}],"ix":2}},{"ty":"fl","bm":0,"hd":true,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[4],"t":120},{"s":[2],"t":217}],"ix":2},"c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[21.47,6.768],"t":120,"ti":[2.515,1.1],"to":[-3.325,-1.455]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":533,"ti":[0.201,0.088],"to":[0,0]},{"s":[21.47,6.768],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2,"parent":1}]},{"nm":"","id":"2","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10198.197998046875,10211.68099975586],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20,0],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":127},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":533},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":590},{"s":[20,0],"t":600}],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[21.47,6.768],"t":120,"ti":[2.515,1.1],"to":[-3.325,-1.455]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":217,"ti":[0,0],"to":[-2.667,-1.167]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":533,"ti":[0.201,0.088],"to":[0,0]},{"s":[21.47,6.768],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2,"parent":1}]},{"nm":"","id":"3","layers":[{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[10,53.985],[52.086,53.985],[10,53.985]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":4,"ix":2},"c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[31.043,31.992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,442.043],"t":217,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":333,"ti":[0,0],"to":[0,0]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[113.658,500.043],"t":433,"ti":[0,0],"to":[0,0]},{"s":[113.658,442.043],"t":533}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1}]},{"nm":"","id":"5","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[130.549,498.039],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50,50],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":533},{"s":[50,50],"t":590}],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10226.417,10218.701],"t":533,"ti":[-0.167,-2.833],"to":[0.167,2.833]},{"s":[10227.417,10235.701],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":2,"parent":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":3,"parent":2},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":4,"parent":3},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[155.709,236.971],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":5,"parent":4},{"ty":4,"nm":"Path","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,-10.728],[0,0],[10.727,0],[0,0],[0,10.726],[0,0],[-10.726,0],[0,0]],"o":[[10.727,0],[0,0],[0,10.726],[0,0],[-10.726,0],[0,0],[0,-10.728],[0,0],[10.727,0]],"v":[[99.436,-226.971],[118.889,-207.516],[118.889,207.517],[99.436,226.972],[-99.435,226.972],[-118.889,207.517],[-118.889,-207.516],[-99.435,-226.971],[99.436,-226.971]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":4,"ix":2},"c":{"a":0,"k":[0.502,0.5255,0.5451],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":6,"parent":5},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[102.742,436.179],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":7,"parent":3},{"ty":4,"nm":"Group","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[10.727,0],[0,0],[0,-10.728],[0,0],[-10.726,0],[0,0],[0,10.727],[0,0],[10.727,0]],"o":[[0,0],[-10.726,0],[0,0],[0,10.727],[0,0],[10.727,0],[0,0],[0,-10.728],[0,0]],"v":[[152.403,-426.179],[-46.468,-426.179],[-65.922,-406.724],[-65.922,8.309],[-46.468,27.764],[152.403,27.764],[171.857,8.309],[171.857,-406.724],[152.403,-426.179]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,-6.316],[0,0],[6.315,0],[0,0],[0,6.316],[0,0],[-6.316,0],[0,0]],"o":[[6.315,0],[0,0],[0,6.316],[0,0],[-6.316,0],[0,0],[0,-6.316],[0,0],[6.315,0]],"v":[[152.403,-418.179],[163.857,-406.724],[163.857,8.309],[152.403,19.764],[-46.468,19.764],[-57.922,8.309],[-57.922,-406.724],[-46.468,-418.179],[152.403,-418.179]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"fl","bm":0,"hd":true,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9451,0.9529,0.9569],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":8,"parent":7},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[104.741,434.179],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":9,"parent":2},{"ty":4,"nm":"Path","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,8.521],[0,0],[-8.521,0],[0,0],[0,-8.522],[0,0],[8.521,0],[0,0]],"o":[[-8.521,0],[0,0],[0,-8.522],[0,0],[8.521,0],[0,0],[0,8.521],[0,0],[-8.521,0]],"v":[[-48.467,25.764],[-63.922,10.309],[-63.922,-404.724],[-48.467,-420.179],[150.404,-420.179],[165.858,-404.724],[165.858,10.309],[150.404,25.764],[-48.467,25.764]]}}},{"ty":"fl","bm":0,"hd":true,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":10,"parent":9}]},{"nm":"","id":"6","layers":[{"ty":0,"nm":"circle 2 (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":602},{"s":[0],"t":603}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"4","ind":1},{"ty":0,"nm":"PhoneOutlines","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"5","ind":2}]},{"nm":"","id":"4","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[89.207,89.207],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10196.825,10220.283],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":2,"parent":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[90.58,80.605],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":3,"parent":2},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20,0],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":127},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":533},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":590},{"s":[20,0],"t":600}],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[21.47,6.768],"t":120,"ti":[2.515,1.1],"to":[-3.325,-1.455]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":217,"ti":[0,0],"to":[-2.667,-1.167]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":533,"ti":[0.201,0.088],"to":[0,0]},{"s":[21.47,6.768],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4,"parent":3}]},{"nm":"","id":"8","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[130.549,498.039],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[50,50],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":533},{"s":[50,50],"t":590}],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10226.417,10218.701],"t":533,"ti":[-0.167,-2.833],"to":[0.167,2.833]},{"s":[10227.417,10235.701],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":2,"parent":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":3,"parent":2},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":4,"parent":3},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[155.709,236.971],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":5,"parent":4},{"ty":4,"nm":"Path","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,-10.728],[0,0],[10.727,0],[0,0],[0,10.726],[0,0],[-10.726,0],[0,0]],"o":[[10.727,0],[0,0],[0,10.726],[0,0],[-10.726,0],[0,0],[0,-10.728],[0,0],[10.727,0]],"v":[[99.436,-226.971],[118.889,-207.516],[118.889,207.517],[99.436,226.972],[-99.435,226.972],[-118.889,207.517],[-118.889,-207.516],[-99.435,-226.971],[99.436,-226.971]]}}},{"ty":"st","bm":0,"hd":true,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":4,"ix":2},"c":{"a":0,"k":[0.502,0.5255,0.5451],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":6,"parent":5},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[102.742,436.179],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":7,"parent":3},{"ty":4,"nm":"Group","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":true,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[10.727,0],[0,0],[0,-10.728],[0,0],[-10.726,0],[0,0],[0,10.727],[0,0],[10.727,0]],"o":[[0,0],[-10.726,0],[0,0],[0,10.727],[0,0],[10.727,0],[0,0],[0,-10.728],[0,0]],"v":[[152.403,-426.179],[-46.468,-426.179],[-65.922,-406.724],[-65.922,8.309],[-46.468,27.764],[152.403,27.764],[171.857,8.309],[171.857,-406.724],[152.403,-426.179]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,-6.316],[0,0],[6.315,0],[0,0],[0,6.316],[0,0],[-6.316,0],[0,0]],"o":[[6.315,0],[0,0],[0,6.316],[0,0],[-6.316,0],[0,0],[0,-6.316],[0,0],[6.315,0]],"v":[[152.403,-418.179],[163.857,-406.724],[163.857,8.309],[152.403,19.764],[-46.468,19.764],[-57.922,8.309],[-57.922,-406.724],[-46.468,-418.179],[152.403,-418.179]]}}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"fl","bm":0,"hd":true,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":8,"parent":7},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[104.741,434.179],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":9,"parent":2},{"ty":4,"nm":"Path","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,8.648],[0,0],[-8.648,0],[0,0],[0,-8.649],[0,0],[8.648,0],[0,0]],"o":[[-8.648,0],[0,0],[0,-8.649],[0,0],[8.648,0],[0,0],[0,8.648],[0,0],[-8.648,0]],"v":[[-49.951,29.093],[-65.637,13.407],[-65.637,-407.822],[-49.951,-423.508],[151.889,-423.508],[167.573,-407.822],[167.573,13.407],[151.889,29.093],[-49.951,29.093]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":10,"parent":9}]},{"nm":"","id":"9","layers":[{"ty":0,"nm":"circle 7 (In/Out)","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":119},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":602},{"s":[0],"t":603}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"7","ind":1},{"ty":0,"nm":".black","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"8","ind":2}]},{"nm":"","id":"7","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[89.207,89.207],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10196.825,10220.283],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":2,"parent":1},{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[90.58,80.605],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":3,"parent":2},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20,0],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":127},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":533},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":590},{"s":[20,0],"t":600}],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[21.47,6.768],"t":120,"ti":[2.515,1.1],"to":[-3.325,-1.455]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":217,"ti":[0,0],"to":[-2.667,-1.167]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":533,"ti":[0.201,0.088],"to":[0,0]},{"s":[21.47,6.768],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4,"parent":3}]},{"nm":"","id":"11","layers":[{"ty":0,"nm":"sim 2","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10009.509,10012.511],"ix":2},"s":{"a":0,"k":[-100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[9989.043,10023.698],"t":300,"ti":[-7,0],"to":[7,0]},{"s":[10031.043,10023.698],"t":400}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":300},{"s":[100],"t":333}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"10","ind":1}]},{"nm":"","id":"10","layers":[{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.936,3.244],[0.936,3.244],[0.936,-3.244],[-0.936,-3.244],[-0.936,3.244]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[10.44400024414062,11.22399997711182],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.937,1.961],[0.937,1.961],[0.937,-1.961],[-0.937,-1.961],[-0.937,1.961]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[14.39500045776367,12.50699996948242],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2},{"ty":4,"nm":"Group 3","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.937,-2.575],[-0.937,-2.575],[-0.937,2.575],[0.937,2.575],[0.937,-2.575]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[14.39500045776367,5.40500020980835],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":3},{"ty":4,"nm":"Group 4","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.936,-1.288],[-0.936,-1.288],[-0.936,1.288],[0.936,1.288],[0.936,-1.288]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[10.44400024414062,4.118000030517578],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4},{"ty":4,"nm":"Group 5","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.485],[0,0],[-1.011,0],[0,0],[0,1.011],[0,0],[1.011,0],[0,0],[0.344,-0.344],[0,0],[0,-0.485]],"o":[[0,0],[0,1.011],[0,0],[1.011,0],[0,0],[0,-1.011],[0,0],[-0.485,0],[0,0],[-0.343,0.343],[0,0]],"v":[[-9.259,-4.36],[-9.259,10.431],[-7.428,12.261],[7.428,12.261],[9.259,10.431],[9.259,-10.431],[7.428,-12.261],[-1.358,-12.261],[-2.652,-11.725],[-8.723,-5.654],[-9.259,-4.36]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[9.508999824523926,12.51099967956543],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":5}]},{"nm":"","id":"13","layers":[{"ty":0,"nm":"sim","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10009.509,10012.511],"ix":2},"s":{"a":0,"k":[-100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10167.526,10212.411],"t":300,"ti":[-7,0],"to":[7,0]},{"s":[10209.526,10212.411],"t":400}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":300},{"s":[100],"t":333}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"12","ind":1}]},{"nm":"","id":"12","layers":[{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.936,3.244],[0.936,3.244],[0.936,-3.244],[-0.936,-3.244],[-0.936,3.244]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[10.44400024414062,11.22399997711182],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.937,1.961],[0.937,1.961],[0.937,-1.961],[-0.937,-1.961],[-0.937,1.961]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[14.39500045776367,12.50699996948242],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2},{"ty":4,"nm":"Group 3","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.937,-2.575],[-0.937,-2.575],[-0.937,2.575],[0.937,2.575],[0.937,-2.575]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[14.39500045776367,5.40500020980835],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":3},{"ty":4,"nm":"Group 4","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.936,-1.288],[-0.936,-1.288],[-0.936,1.288],[0.936,1.288],[0.936,-1.288]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8235,0.8902,0.9882],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[10.44400024414062,4.118000030517578],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":4},{"ty":4,"nm":"Group 5","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-0.485],[0,0],[-1.011,0],[0,0],[0,1.011],[0,0],[1.011,0],[0,0],[0.344,-0.344],[0,0],[0,-0.485]],"o":[[0,0],[0,1.011],[0,0],[1.011,0],[0,0],[0,-1.011],[0,0],[-0.485,0],[0,0],[-0.343,0.343],[0,0]],"v":[[-9.259,-4.36],[-9.259,10.431],[-7.428,12.261],[7.428,12.261],[9.259,10.431],[9.259,-10.431],[7.428,-12.261],[-1.358,-12.261],[-2.652,-11.725],[-8.723,-5.654],[-9.259,-4.36]]}}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[9.508999824523926,12.51099967956543],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":5}]},{"nm":"","id":"14","layers":[{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,-2.099],[0,0],[1.089,0],[0,0],[0,1.089],[0,0],[-2.1,0],[0,0],[0,-2.099]],"o":[[0,0],[0,1.089],[0,0],[-1.088,0],[0,0],[0,-2.099],[0,0],[2.1,0],[0,0]],"v":[[15.558,-20.691],[15.558,22.521],[13.587,24.492],[-13.587,24.492],[-15.558,22.521],[-15.558,-20.691],[-11.756,-24.492],[11.757,-24.492],[15.558,-20.691]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.3725,0.3882,0.4078],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[31.04299926757812,29.49300003051758],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1}]},{"nm":"","id":"16","layers":[{"ty":0,"nm":"tool","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[10017.542,10027.439],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10225.605,10251.95],"t":167,"ti":[0,0],"to":[0,-9.833]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10225.605,10192.95],"t":217,"ti":[0,-9.833],"to":[0,0]},{"s":[10225.605,10251.95],"t":300}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":167},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":183},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":263},{"s":[0],"t":283}],"ix":2}},"ef":[],"w":20000,"h":20000,"refId":"15","ind":1}]},{"nm":"","id":"15","layers":[{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[17.542,5],[17.542,24.794]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":1},{"ty":4,"nm":"Group 2","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[-6.927,0],[0,-6.927],[6.927,0],[0,6.927],[-6.927,0]],"o":[[6.927,0],[0,6.927],[-6.927,0],[0,-6.927],[6.927,0]],"v":[[0,-12.542],[12.542,0],[0,12.542],[-12.542,0],[0,-12.542]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[17.54199981689453,37.33599853515625],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2}]},{"nm":"","id":"17","layers":[{"ty":3,"nm":"","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10198.197998046875,10211.68099975586],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"ind":1},{"ty":4,"nm":"Ellipse Path 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"el","bm":0,"hd":false,"nm":"","d":1,"p":{"a":0,"k":[0,0],"ix":2},"s":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[20,0],"t":120},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":127},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[164,164],"t":533},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[30,30],"t":590},{"s":[20,0],"t":600}],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0,0,0],"ix":2},"r":1,"o":{"a":0,"k":100,"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[21.47,6.768],"t":120,"ti":[2.515,1.1],"to":[-3.325,-1.455]},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":217},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0,0],"t":533,"ti":[0.201,0.088],"to":[0,0]},{"s":[21.47,6.768],"t":590}],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}}]}],"ind":2,"parent":1}]},{"nm":"","id":"18","layers":[{"ty":4,"nm":"Group 1","sr":1,"st":0,"op":633.3333333333333,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0},"p":{"a":0,"k":[10000,10000],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":2}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[24.5,5],[5,5]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"c":{"a":0,"k":[0.4,0.6157,0.9647],"ix":2}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"s":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","a":{"a":0,"k":[14.75,5],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"sk":{"a":0,"k":0,"ix":2},"p":{"a":0,"k":[219.678,218.226],"ix":2},"r":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":10},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":13},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":33},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":37},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":57},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":80},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":83},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":100},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":103},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":120},{"s":[100],"t":123}],"ix":2}}]}],"ind":1}]}]} \ No newline at end of file diff --git a/Euicc/res/values/strings.xml b/Euicc/res/values/strings.xml deleted file mode 100644 index 74436545..00000000 --- a/Euicc/res/values/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - {\"sim_illustration_lottie_mappings\":[{\"devices\":[\"garnet\"],\"illustration_lottie\":\"sim_illustration_lottie_bottom\"}]} - {\"sim-slot-mappings\":[{\"devices\":[\"garnet\",\"XIG05\"],\"esim-slot-ids\":[1],\"psim-slot-ids\":[0]}]} - diff --git a/Euicc/src/org/lineageos/euicc/BootCompletedReceiver.kt b/Euicc/src/org/lineageos/euicc/BootCompletedReceiver.kt deleted file mode 100644 index 309b417b..00000000 --- a/Euicc/src/org/lineageos/euicc/BootCompletedReceiver.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2021 The LineageOS Project - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.lineageos.euicc - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.util.Log - -class BootCompletedReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - Log.d(TAG, "Received boot completed intent") - EuiccDisabler.enableOrDisableEuicc(context) - } - - companion object { - private const val TAG = "XiaomiEuiccBootReceiver" - } -} diff --git a/Euicc/src/org/lineageos/euicc/EuiccDisabler.kt b/Euicc/src/org/lineageos/euicc/EuiccDisabler.kt deleted file mode 100644 index cc1d39de..00000000 --- a/Euicc/src/org/lineageos/euicc/EuiccDisabler.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2021 The LineageOS Project - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.lineageos.euicc - -import android.content.Context -import android.content.pm.PackageManager -import android.content.pm.PackageManager.PackageInfoFlags -import android.util.Log -import android.os.SystemProperties - -object EuiccDisabler { - private const val TAG = "XiaomiEuiccDisabler" - - private val EUICC_DEPENDENCIES = listOf( - "com.google.android.gms", - "com.google.android.gsf", - ) - - private val EUICC_PACKAGES = listOf( - "com.google.android.euicc", - ) - - private fun isInstalledAndEnabled(pm: PackageManager, pkgName: String) = runCatching { - val info = pm.getPackageInfo(pkgName, PackageInfoFlags.of(0)) - Log.d(TAG, "package $pkgName installed, enabled = ${info.applicationInfo?.enabled}") - info.applicationInfo?.enabled ?: false - }.getOrDefault(false) - - fun enableOrDisableEuicc(context: Context) { - val pm = context.packageManager - val sku = SystemProperties.get("ro.boot.product.hardware.sku") - val disable = if (sku == "IN" || sku == "CN") { - Log.d(TAG, "Disabling apps due to IN or CN SKU") - true // Disable if SKU is IN or CN - } else { - EUICC_DEPENDENCIES.any { !isInstalledAndEnabled(pm, it) } - } - val flag = if (disable) { - PackageManager.COMPONENT_ENABLED_STATE_DISABLED - } else { - PackageManager.COMPONENT_ENABLED_STATE_ENABLED - } - - for (pkg in EUICC_PACKAGES) { - pm.setApplicationEnabledSetting(pkg, flag, 0) - } - } -} diff --git a/Euicc/src/org/lineageos/euicc/EuiccReceiver.kt b/Euicc/src/org/lineageos/euicc/EuiccReceiver.kt deleted file mode 100644 index b11e3c79..00000000 --- a/Euicc/src/org/lineageos/euicc/EuiccReceiver.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2023 The LineageOS Project - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.lineageos.euicc - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.util.Log - -class EuiccReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - Log.d(TAG, "Received PARTNER_CUSTOMIZATION intent") - } - - companion object { - private const val TAG = "XiaomiEuiccReceiver" - } -} diff --git a/EuiccGarnet/Android.bp b/EuiccGarnet/Android.bp new file mode 100644 index 00000000..bf2eb015 --- /dev/null +++ b/EuiccGarnet/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2023-2025 Paranoid Android +// +// SPDX-License-Identifier: Apache-2.0 +// + +android_app { + name: "EuiccGarnet", + certificate: "platform", + srcs: ["src/**/*.kt"], + platform_apis: true, + privileged: true, + system_ext_specific: true, + static_libs: [ + "androidx.core_core", + "androidx.preference_preference", + "org.lineageos.settings.resources", + "SettingsLib", + "qcrilhook", + ], + required: [ + "privapp_whitelist_com.xiaomi.settings.xml", + ], + optional_uses_libs: [ + "com.qualcomm.qcrilhook", + "com.qti.extphone.extphonelib", + ], +} + +prebuilt_etc { + name: "privapp_whitelist_com.xiaomi.settings.xml", + src: "permissions/privapp_whitelist_com.xiaomi.settings.xml", + relative_install_path: "permissions", + system_ext_specific: true, +} diff --git a/EuiccGarnet/AndroidManifest.xml b/EuiccGarnet/AndroidManifest.xml new file mode 100644 index 00000000..6e2f75b8 --- /dev/null +++ b/EuiccGarnet/AndroidManifest.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EuiccGarnet/permissions/privapp_whitelist_com.xiaomi.settings.xml b/EuiccGarnet/permissions/privapp_whitelist_com.xiaomi.settings.xml new file mode 100644 index 00000000..fd7711e7 --- /dev/null +++ b/EuiccGarnet/permissions/privapp_whitelist_com.xiaomi.settings.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/EuiccGarnet/res/drawable/ic_sim_card_download.xml b/EuiccGarnet/res/drawable/ic_sim_card_download.xml new file mode 100644 index 00000000..1da4bf38 --- /dev/null +++ b/EuiccGarnet/res/drawable/ic_sim_card_download.xml @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/EuiccGarnet/res/values/strings.xml b/EuiccGarnet/res/values/strings.xml new file mode 100644 index 00000000..f2d24cda --- /dev/null +++ b/EuiccGarnet/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + + Xiaomi Parts + + + eSIM + Enable eSIM + Enabled + Disabled + eSIM is not supported on this device + eSIM in use + Please disable or remove your current eSIM profile before proceeding + Enabling eSIM will disable the second physical SIM slot + This device does not support eSIM functionality + diff --git a/EuiccGarnet/res/xml/settings_esim.xml b/EuiccGarnet/res/xml/settings_esim.xml new file mode 100644 index 00000000..f24cbf5c --- /dev/null +++ b/EuiccGarnet/res/xml/settings_esim.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/EuiccGarnet/src/com/xiaomi/settings/BootCompletedReceiver.kt b/EuiccGarnet/src/com/xiaomi/settings/BootCompletedReceiver.kt new file mode 100644 index 00000000..38d564cf --- /dev/null +++ b/EuiccGarnet/src/com/xiaomi/settings/BootCompletedReceiver.kt @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.xiaomi.settings + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import com.xiaomi.settings.telephony.EsimController + +class BootCompletedReceiver : BroadcastReceiver() { + + companion object { + private const val TAG = "XiaomiParts" + private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) + } + + override fun onReceive(context: Context, intent: Intent) { + if (DEBUG) Log.d(TAG, "Received boot completed intent: ${intent.action}") + when (intent.action) { + Intent.ACTION_BOOT_COMPLETED -> onBootCompleted(context) + } + } + + private fun onBootCompleted(context: Context) { + // Telephony + EsimController.getInstance(context).onBootCompleted() + } +} diff --git a/EuiccGarnet/src/com/xiaomi/settings/SummaryProvider.kt b/EuiccGarnet/src/com/xiaomi/settings/SummaryProvider.kt new file mode 100644 index 00000000..1f772575 --- /dev/null +++ b/EuiccGarnet/src/com/xiaomi/settings/SummaryProvider.kt @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2019 The Android Open Source Project + * SPDX-FileCopyrightText: 2025 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.xiaomi.settings + +import android.content.ContentProvider +import android.content.ContentValues +import android.database.Cursor +import android.net.Uri +import android.os.Bundle +import com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY +import com.xiaomi.settings.telephony.EsimController + +class SummaryProvider : ContentProvider() { + + private val esimController by lazy { EsimController.getInstance(requireContext()) } + + override fun call(method: String, uri: String?, extras: Bundle?): Bundle { + val bundle = Bundle() + val summary = + when (method) { + KEY_ESIM -> getESimSummary() + else -> throw IllegalArgumentException("Unknown method: $method") + } + bundle.putString(META_DATA_PREFERENCE_SUMMARY, summary) + return bundle + } + + override fun onCreate(): Boolean = true + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String?, + ): Cursor? = throw UnsupportedOperationException() + + override fun getType(uri: Uri): String? = throw UnsupportedOperationException() + + override fun insert(uri: Uri, values: ContentValues?): Uri? = + throw UnsupportedOperationException() + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = + throw UnsupportedOperationException() + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array?, + ): Int = throw UnsupportedOperationException() + + private fun getESimSummary(): String { + val context = context ?: return "" + return when { + esimController.getEsimEnabled() -> context.getString(R.string.esim_summary_enabled) + esimController.getEsimSupported() -> context.getString(R.string.esim_summary_disabled) + else -> context.getString(R.string.esim_summary_unsupported) + } + } + + companion object { + private const val KEY_ESIM = "esim" + } +} diff --git a/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimController.kt b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimController.kt new file mode 100644 index 00000000..28032247 --- /dev/null +++ b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimController.kt @@ -0,0 +1,220 @@ +/* + * SPDX-FileCopyrightText: 2025 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.xiaomi.settings.telephony + +import android.app.ActivityThread +import android.content.Context +import android.content.pm.PackageManager +import android.os.SystemProperties +import android.telephony.SubscriptionManager +import android.util.Log +import dalvik.system.DexClassLoader +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy + +class EsimController private constructor(private val context: Context) { + + companion object { + private const val TAG = "EsimController" + private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) + + private const val PROPERTY_DEVICE_INFO_HW = "ro.boot.hwc" + + private const val MIRILHOOK_CLASS_NAME = "com.xiaomi.mirilhook.MiRilHook" + private const val MIRILHOOK_JAR_PATH = "/system_ext/framework/mirilhook.jar" + private const val QCRILHOOKCALLBACK_CLASS_NAME = "com.qualcomm.qcrilhook.QcRilHookCallback" + private const val QCRILHOOK_JAR_PATH = "/system_ext/framework/qcrilhook.jar" + + @Volatile private var instance: EsimController? = null + + fun getInstance(context: Context): EsimController { + return instance + ?: synchronized(this) { + instance ?: EsimController(context.applicationContext).also { instance = it } + } + } + } + + private var miRilJarLoader: DexClassLoader? = null + private var miRilHookClass: Class<*>? = null + private var miRilHookObj: Any? = null + private var qcRilJarLoader: DexClassLoader? = null + private var qcRilHookCallbackClass: Class<*>? = null + private var qcRilHookCallbackObj: Any? = null + + fun onBootCompleted() { + if (DEBUG) Log.d(TAG, "onBootCompleted") + setupHook() + updateEuicc() + } + + fun getEsimActive(): Boolean { + val subscriptionManager = + context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager + val subscriptionInfoList = subscriptionManager?.activeSubscriptionInfoList ?: return false + + for (subscriptionInfo in subscriptionInfoList) { + if (subscriptionInfo.isEmbedded) { + if (DEBUG) + Log.d( + TAG, + "Found eSIM profile: ${subscriptionInfo.displayName}, ${subscriptionInfo.carrierName}", + ) + return true + } + } + if (DEBUG) Log.d(TAG, "No eSIM profiles found.") + return false + } + + fun getEsimSupported(): Boolean { + val hw = SystemProperties.get(PROPERTY_DEVICE_INFO_HW) + return hw == "JP" || hw == "GL" + } + + fun getEsimEnabled(): Boolean { + return getEsimSupported() && (callMiRilHookMethod("onGetEsimStatus", -1) as? Int ?: -1) == 0 + } + + fun setEsimEnabled(isEnabled: Boolean) { + if (DEBUG) Log.d(TAG, "setEsimEnabled, isEnabled = $isEnabled") + callMiRilHookMethod("onHookUimPowerReqEx", false, 0, 2, -1) + callMiRilHookMethod("onSetEsimStatus", -1, if (isEnabled) 0 else 1, true) + callMiRilHookMethod("onHookUimPowerReqEx", false, 1, 2, if (isEnabled) 1 else 0) + updateEuicc() + } + + private fun setupHook() { + if (DEBUG) Log.d(TAG, "setupHook, context = $context") + if (!getEsimSupported()) { + if (DEBUG) Log.d(TAG, "getEsimSupported is false") + return + } + + miRilJarLoader = + miRilJarLoader + ?: runCatching { + DexClassLoader( + MIRILHOOK_JAR_PATH, + context.getDir("jar", 0).absolutePath, + null, + context.classLoader, + ) + } + .onFailure { e -> + if (DEBUG) Log.d(TAG, "Failed to initialize miRilJarLoader: $e") + } + .getOrNull() + + miRilHookClass = + miRilHookClass + ?: runCatching { miRilJarLoader?.loadClass(MIRILHOOK_CLASS_NAME) } + .onFailure { e -> if (DEBUG) Log.d(TAG, "Failed to load miRilHookClass: $e") } + .getOrNull() + + qcRilJarLoader = + qcRilJarLoader + ?: runCatching { + DexClassLoader( + QCRILHOOK_JAR_PATH, + context.getDir("jar", 0).absolutePath, + null, + context.classLoader, + ) + } + .onFailure { e -> + if (DEBUG) Log.d(TAG, "Failed to initialize qcRilJarLoader: $e") + } + .getOrNull() + + qcRilHookCallbackClass = + qcRilHookCallbackClass + ?: runCatching { qcRilJarLoader?.loadClass(QCRILHOOKCALLBACK_CLASS_NAME) } + .onFailure { e -> + if (DEBUG) Log.d(TAG, "Failed to load qcRilHookCallbackClass: $e") + } + .getOrNull() + + qcRilHookCallbackObj = + qcRilHookCallbackObj + ?: runCatching { + Proxy.newProxyInstance( + context.classLoader, + arrayOf(qcRilHookCallbackClass), + QcRilHookCbMethodProxy(), + ) + } + .onFailure { e -> + if (DEBUG) Log.d(TAG, "Failed to initialize qcRilHookCallbackObj: $e") + } + .getOrNull() + + miRilHookObj = + miRilHookObj + ?: miRilHookClass + ?.getConstructor( + Context::class.java, + qcRilHookCallbackClass, + String::class.java, + ) + ?.let { constructor -> + runCatching { + constructor.newInstance( + context, + qcRilHookCallbackObj, + ActivityThread.currentPackageName(), + ) + } + .onFailure { e -> + if (DEBUG) Log.d(TAG, "Failed to initialize miRilHookObj: $e") + } + .getOrNull() + } + } + + private fun callMiRilHookMethod(methodName: String, defObj: Any?, vararg args: Any?): Any? { + return try { + val parameterTypes = + args + .map { it?.javaClass?.kotlin?.javaPrimitiveType ?: it?.javaClass } + .toTypedArray() + miRilHookClass?.getMethod(methodName, *parameterTypes)?.invoke(miRilHookObj, *args) + } catch (e: Exception) { + if (DEBUG) Log.d(TAG, "callMiRilHookMethod failed: $methodName, error: $e") + defObj + } + } + + private fun updateEuicc() { + context.packageManager?.setApplicationEnabledSetting( + "com.google.android.euicc", + if (getEsimEnabled()) PackageManager.COMPONENT_ENABLED_STATE_ENABLED + else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + 0, + ) + } + + fun dispose() { + if (DEBUG) Log.d(TAG, "dispose") + try { + miRilHookClass?.getMethod("dispose")?.invoke(miRilHookObj) + } catch (e: Exception) { + if (DEBUG) Log.d(TAG, "dispose method failed, error: $e") + } + } + + inner class QcRilHookCbMethodProxy : InvocationHandler { + override fun invoke(proxy: Any, method: Method, args: Array?): Any? { + val methodName = method.name + if (DEBUG) Log.d(TAG, "QcRilHookCbMethodProxy callbackMethod name: $methodName") + when (methodName) { + "onQcRilHookReady" -> updateEuicc() + } + return null + } + } +} diff --git a/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsActivity.kt b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsActivity.kt new file mode 100644 index 00000000..4a142fff --- /dev/null +++ b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsActivity.kt @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2025 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.xiaomi.settings.telephony + +import android.os.Bundle +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity +import com.android.settingslib.collapsingtoolbar.R + +class EsimSettingsActivity : CollapsingToolbarBaseActivity() { + + companion object { + private const val TAG = "EsimSettingsActivity" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportFragmentManager + .beginTransaction() + .replace(R.id.content_frame, EsimSettingsFragment(), TAG) + .commit() + } +} diff --git a/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsFragment.kt b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsFragment.kt new file mode 100644 index 00000000..3011d67a --- /dev/null +++ b/EuiccGarnet/src/com/xiaomi/settings/telephony/EsimSettingsFragment.kt @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.xiaomi.settings.telephony + +import android.os.Bundle +import android.util.Log +import android.widget.CompoundButton +import androidx.appcompat.app.AlertDialog +import com.android.settingslib.widget.FooterPreference +import com.android.settingslib.widget.MainSwitchPreference +import com.android.settingslib.widget.SettingsBasePreferenceFragment +import com.xiaomi.settings.R + +class EsimSettingsFragment : + SettingsBasePreferenceFragment(), CompoundButton.OnCheckedChangeListener { + + companion object { + private const val TAG = "EsimSettingsFragment" + private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) + } + + private val esimController by lazy { EsimController.getInstance(requireContext()) } + + private val switchBar by lazy { findPreference("esim_enable")!! } + private val footerPref by lazy { findPreference("esim_footer")!! } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + if (DEBUG) Log.d(TAG, "onCreatePreferences") + setPreferencesFromResource(R.xml.settings_esim, rootKey) + + if (esimController.getEsimSupported()) { + switchBar.addOnSwitchChangeListener(this) + switchBar.isChecked = esimController.getEsimEnabled() + switchBar.isEnabled = true + footerPref.title = getString(R.string.esim_footer_note) + } + } + + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + if (DEBUG) Log.d(TAG, "onCheckedChanged: $isChecked") + if (esimController.getEsimActive()) { + if (isChecked) return + switchBar.isChecked = true + AlertDialog.Builder(requireContext()) + .setTitle(R.string.esim_warning_title) + .setMessage(R.string.esim_warning_message) + .setPositiveButton(android.R.string.ok, null) + .setCancelable(false) + .show() + } else { + esimController.setEsimEnabled(isChecked) + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..c05b6c8a --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Xiaomi Redmi Note 13 Pro 5G (garnet) + +The **Xiaomi Redmi Note 13 Pro 5G / Poco X6 5G** (codenamed _garnet_) is a mid-range smartphone from Xiaomi, released in September 2023. + +## Device Specifications + +| Feature | Specification | +| :--- | :--- | +| **SoC** | Qualcomm Snapdragon 7s Gen 2 (SM7435) | +| **CPU** | Octa-core (4x2.40 GHz Cortex-A78 & 4x1.95 GHz Cortex-A55) | +| **GPU** | Adreno 710 | +| **Memory** | 8 GB / 12 GB | +| **Storage** | 256 GB / 512 GB UFS 2.2 | +| **Battery** | Non-removable Li-Po 5100 mAh | +| **Charging** | 67W wired PD3.0 | +| **Display** | 6.67" AMOLED, 120Hz, Dolby Vision, 1220 x 2712 pixels | +| **Rear Camera** | 200 / 64 MP (wide) + 8 MP (ultrawide) + 2 MP (macro) | +| **Front Camera** | 16 MP (wide) | +| **Dimensions** | 161.2 x 74.2 x 8 mm | +| **Weight** | 187 g | + +## Device Picture + +![Xiaomi Redmi Note 13 Pro 5G](logo.png) + +## Copyright + +``` +# +# Copyright (C) 2024 The LineageOS Project +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/blob-patches/mirilhook.patch b/blob-patches/mirilhook.patch new file mode 100644 index 00000000..60bcb8c9 --- /dev/null +++ b/blob-patches/mirilhook.patch @@ -0,0 +1,33 @@ +From 27fb0061802c7ce5cc210ceed4076ff8d3edbdc2 Mon Sep 17 00:00:00 2001 +From: Fabian Leutenegger +Date: Thu, 24 Apr 2025 08:01:26 +0200 +Subject: [PATCH] Patch MiRilHook to allow its use from com.xiaomi.settings + +--- + smali/com/xiaomi/mirilhook/MiRilHook.smali | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/smali/com/xiaomi/mirilhook/MiRilHook.smali b/smali/com/xiaomi/mirilhook/MiRilHook.smali +index fb6a844..0dca69d 100644 +--- a/smali/com/xiaomi/mirilhook/MiRilHook.smali ++++ b/smali/com/xiaomi/mirilhook/MiRilHook.smali +@@ -1385,7 +1385,7 @@ + + .line 8100 + :cond_0 +- const-string v4, "com.xiaomi.mtb" ++ const-string v4, "com.xiaomi.settings" + + invoke-virtual {v4, v3}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z + +@@ -5897,7 +5897,7 @@ + .line 619 + sget-object v0, Lcom/xiaomi/mirilhook/MiRilHook;->mHookUserWhiteList:Ljava/util/ArrayList; + +- const-string v2, "com.xiaomi.mtb" ++ const-string v2, "com.xiaomi.settings" + + invoke-virtual {v0, v2}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z + +-- +2.48.1 diff --git a/configs/audio/audio_effects.xml b/configs/audio/audio_effects.xml index f114f137..505c5924 100644 --- a/configs/audio/audio_effects.xml +++ b/configs/audio/audio_effects.xml @@ -73,6 +73,16 @@ + + + + + + + + + + @@ -119,22 +129,40 @@ + + + + + + + + + + + + + + + + + + diff --git a/configs/audio/mixer_paths_overlay_static.xml b/configs/audio/mixer_paths_overlay_static.xml index 05a661fc..6d2b128a 100644 --- a/configs/audio/mixer_paths_overlay_static.xml +++ b/configs/audio/mixer_paths_overlay_static.xml @@ -195,12 +195,6 @@ - - - - - - diff --git a/configs/audio/mixer_paths_overlay_static_foursemi.xml b/configs/audio/mixer_paths_overlay_static_foursemi.xml index 8a277c70..9469ad92 100644 --- a/configs/audio/mixer_paths_overlay_static_foursemi.xml +++ b/configs/audio/mixer_paths_overlay_static_foursemi.xml @@ -185,10 +185,6 @@ - - - - diff --git a/configs/hidl/device_framework_matrix.xml b/configs/hidl/device_framework_matrix.xml new file mode 100644 index 00000000..f157ff40 --- /dev/null +++ b/configs/hidl/device_framework_matrix.xml @@ -0,0 +1,18 @@ + + + vendor.dolby.hardware.dms + 2.0 + + IDms + default + + + + android.hardware.media.c2 + 1.0 + + IComponentStore + default1 + + + diff --git a/configs/hidl/manifest.xml b/configs/hidl/manifest.xml index 834de0a4..edd8e5c2 100644 --- a/configs/hidl/manifest.xml +++ b/configs/hidl/manifest.xml @@ -517,16 +517,6 @@ @3.0::IRTPService/imsrtpservice - - vendor.qti.qesdhal - hwbinder - 1.1 - - IQesdhal - default - - @1.1::IQesdhal/default - vendor.qti.qspmhal hwbinder diff --git a/configs/power/powerhint.json b/configs/power/powerhint.json index f2ba67ac..9c51ba70 100644 --- a/configs/power/powerhint.json +++ b/configs/power/powerhint.json @@ -1,556 +1,591 @@ { - "Nodes": [ - { - "Name": "CPULittleClusterMaxFreq", - "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", - "Values": [ - "1958400", - "1804800", - "1651200", - "1497600", - "1324800", - "1113600", - "940800", - "806400", - "691200" - ], - "DefaultIndex": 0, - "ResetOnInit": true - }, - { - "Name": "CPULittleClusterMinFreq", - "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", - "Values": [ - "1958400", - "1804800", - "1651200", - "1497600", - "1324800", - "1113600", - "940800", - "806400", - "691200" - ], - "ResetOnInit": true - }, - { - "Name": "CPULittleClusterUpRateLimit", - "Path": "/sys/devices/system/cpu/cpufreq/policy0/walt/up_rate_limit_us", - "Values": [ - "500", - "2000" - ], - "ResetOnInit": true - }, - { - "Name": "CPULittleClusterDownRateLimit", - "Path": "/sys/devices/system/cpu/cpufreq/policy0/walt/down_rate_limit_us", - "Values": [ - "20000", - "2000" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBigClusterMaxFreq", - "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq", - "Values": [ - "2400000", - "2304000", - "2208000", - "2112000", - "2054400", - "1900800", - "1651200", - "1497600", - "1344000", - "1190400", - "960000", - "691200" - ], - "DefaultIndex": 0, - "ResetOnInit": true - }, - { - "Name": "CPUBigClusterMinFreq", - "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq", - "Values": [ - "2400000", - "2304000", - "2208000", - "2112000", - "2054400", - "1900800", - "1651200", - "1497600", - "1344000", - "1190400", - "960000", - "691200" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBigClusterUpRateLimit", - "Path": "/sys/devices/system/cpu/cpufreq/policy4/walt/up_rate_limit_us", - "Values": [ - "500", - "2000" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBigClusterDownRateLimit", - "Path": "/sys/devices/system/cpu/cpufreq/policy4/walt/down_rate_limit_us", - "Values": [ - "10000", - "2000" - ], - "ResetOnInit": true - }, - { - "Name": "UclampTAMin", - "Path": "/dev/cpuctl/top-app/cpu.uclamp.min", - "Values": [ - "0", - "30", - "50" - ], - "DefaultIndex": 0, - "ResetOnInit": true - }, - { - "Name": "UclampTALatency", - "Path": "/dev/cpuctl/top-app/cpu.uclamp.latency_sensitive", - "Values": [ - "1", - "0" - ], - "ResetOnInit": true - }, - { - "Name": "UclampFGMin", - "Path": "/dev/cpuctl/foreground/cpu.uclamp.min", - "Values": [ - "0", - "50" - ], - "DefaultIndex": 0, - "ResetOnInit": true - }, - { - "Name": "SchedBusyHystNs", - "Path": "/proc/sys/walt/sched_busy_hyst_ns", - "Values": [ - "99000000", - "3000000", - "0" - ], - "ResetOnInit": true - }, - { - "Name": "SchedBusyHystEnableCPUs", - "Path": "/proc/sys/walt/sched_busy_hysteresis_enable_cpus", - "Values": [ - "255", - "15", - "0" - ], - "ResetOnInit": true - }, - { - "Name": "SchedMinTaskUtilForBoost", - "Path": "/proc/sys/walt/sched_min_task_util_for_boost", - "Values": [ - "0", - "51" - ], - "ResetOnInit": true - }, - { - "Name": "SchedMinTaskUtilForColoc", - "Path": "/proc/sys/walt/sched_min_task_util_for_colocation", - "Values": [ - "0", - "35" - ], - "ResetOnInit": true - }, - { - "Name": "SchedMinTaskUtilForUclamp", - "Path": "/proc/sys/walt/sched_min_task_util_for_uclamp", - "Values": [ - "0", - "51" - ], - "ResetOnInit": true - }, - { - "Name": "GPUMaxFreq", - "Path": "/sys/class/kgsl/kgsl-3d0/devfreq/max_freq", - "Values": [ - "940000000", - "875000000", - "816000000", - "734000000", - "650000000", - "600000000", - "500000000", - "345000000", - "295000000" - ], - "DefaultIndex": 0, - "ResetOnInit": true - }, - { - "Name": "GPUMinFreq", - "Path": "/sys/class/kgsl/kgsl-3d0/devfreq/min_freq", - "Values": [ - "940000000", - "875000000", - "816000000", - "734000000", - "650000000", - "600000000", - "500000000", - "345000000", - "295000000" - ], - "ResetOnInit": true - }, - { - "Name": "GPUForceRailOn", - "Path": "/sys/class/kgsl/kgsl-3d0/force_rail_on", - "Values": [ - "1", - "0" - ], - "ResetOnInit": true - }, - { - "Name": "GPUForceClkOn", - "Path": "/sys/class/kgsl/kgsl-3d0/force_clk_on", - "Values": [ - "1", - "0" - ], - "ResetOnInit": true - }, - { - "Name": "GPUIdleTimer", - "Path": "/sys/class/kgsl/kgsl-3d0/idle_timer", - "Values": [ - "10000", - "58" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBWHystTriggerCount", - "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/hyst_trigger_count", - "Values": [ - "0", - "3" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBWHistMemory", - "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/hist_memory", - "Values": [ - "0", - "20" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBWHystLength", - "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/hyst_length", - "Values": [ - "0", - "10" - ], - "ResetOnInit": true - }, - { - "Name": "CPUBWMinFreq", - "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/boost_freq", - "Values": [ - "2092000", - "1708000", - "1555000", - "1353600", - "1017000", - "768000", - "547000" - ], - "ResetOnInit": true - }, - { - "Name": "L3MinFreq", - "Path": "/sys/devices/system/cpu/bus_dcvs/L3/boost_freq", - "Values": [ - "1440000", - "1420800", - "1382400", - "1248000", - "1190400", - "1056000", - "940800", - "806400", - "652800", - "556800", - "307200" - ], - "ResetOnInit": true - }, - { - "Name": "PowerHALMainState", - "Path": "vendor.powerhal.state", - "Values": [ - "SUSTAINED_PERFORMANCE", - "" - ], - "Type": "Property" - }, - { - "Name": "PowerHALRenderingState", - "Path": "vendor.powerhal.rendering", - "Values": [ - "EXPENSIVE_RENDERING", - "" - ], - "Type": "Property" - } - ], - "Actions": [ - { - "PowerHint": "SUSTAINED_PERFORMANCE", - "Node": "PowerHALMainState", - "Duration": 0, - "Value": "SUSTAINED_PERFORMANCE" - }, - { - "PowerHint": "SUSTAINED_PERFORMANCE", - "Node": "CPUBigClusterMaxFreq", - "Duration": 0, - "Value": "1497600" - }, - { - "PowerHint": "SUSTAINED_PERFORMANCE", - "Node": "CPULittleClusterMaxFreq", - "Duration": 0, - "Value": "1497600" - }, - { - "PowerHint": "SUSTAINED_PERFORMANCE", - "Node": "GPUMaxFreq", - "Duration": 0, - "Value": "940000000" - }, - { - "PowerHint": "INTERACTION", - "Node": "UclampTAMin", - "Duration": 0, - "Value": "30" - }, - { - "PowerHint": "INTERACTION", - "Node": "UclampTALatency", - "Duration": 0, - "Value": "1" - }, - { - "PowerHint": "INTERACTION", - "Node": "SchedBusyHystNs", - "Duration": 0, - "Value": "99000000" - }, - { - "PowerHint": "INTERACTION", - "Node": "SchedBusyHystEnableCPUs", - "Duration": 0, - "Value": "255" - }, - { - "PowerHint": "INTERACTION", - "Node": "SchedMinTaskUtilForBoost", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "SchedMinTaskUtilForColoc", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "SchedMinTaskUtilForUclamp", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "CPUBWHystTriggerCount", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "CPUBWHystLength", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "CPUBWHistMemory", - "Duration": 0, - "Value": "0" - }, - { - "PowerHint": "INTERACTION", - "Node": "CPUBWMinFreq", - "Duration": 0, - "Value": "1555000" - }, - { - "PowerHint": "INTERACTION", - "Node": "L3MinFreq", - "Duration": 0, - "Value": "940800" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "CPULittleClusterUpRateLimit", - "Duration": 0, - "Value": "500" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "CPULittleClusterDownRateLimit", - "Duration": 0, - "Value": "20000" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "CPUBigClusterUpRateLimit", - "Duration": 0, - "Value": "500" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "CPUBigClusterDownRateLimit", - "Duration": 0, - "Value": "10000" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "SchedBusyHystNs", - "Duration": 0, - "Value": "3000000" - }, - { - "PowerHint": "INTERACTIVE", - "Node": "SchedBusyHystEnableCPUs", - "Duration": 0, - "Value": "15" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPUBigClusterMinFreq", - "Duration": 3000, - "Value": "2400000" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPULittleClusterMinFreq", - "Duration": 3000, - "Value": "1958400" - }, - { - "PowerHint": "LAUNCH", - "Node": "UclampTAMin", - "Duration": 3000, - "Value": "50" - }, - { - "PowerHint": "LAUNCH", - "Node": "UclampFGMin", - "Duration": 3000, - "Value": "50" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPUBWHystTriggerCount", - "Duration": 3000, - "Value": "0" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPUBWHystLength", - "Duration": 3000, - "Value": "0" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPUBWHistMemory", - "Duration": 3000, - "Value": "0" - }, - { - "PowerHint": "LAUNCH", - "Node": "CPUBWMinFreq", - "Duration": 3000, - "Value": "2092000" - }, - { - "PowerHint": "LAUNCH", - "Node": "L3MinFreq", - "Duration": 3000, - "Value": "1440000" - }, - { - "PowerHint": "LAUNCH", - "Node": "GPUForceClkOn", - "Duration": 3000, - "Value": "1" - }, - { - "PowerHint": "LAUNCH", - "Node": "GPUForceRailOn", - "Duration": 3000, - "Value": "1" - }, - { - "PowerHint": "LAUNCH", - "Node": "GPUIdleTimer", - "Duration": 3000, - "Value": "10000" - }, - { - "PowerHint": "EXPENSIVE_RENDERING", - "Node": "PowerHALRenderingState", - "Duration": 0, - "Value": "EXPENSIVE_RENDERING" - }, - { - "PowerHint": "EXPENSIVE_RENDERING", - "Node": "GPUMinFreq", - "Duration": 0, - "Value": "500000000" - }, - { - "PowerHint": "Flipendo", - "Node": "CPULittleClusterMaxFreq", - "Duration": 0, - "Value": "1497600" - }, - { - "PowerHint": "Flipendo", - "Node": "CPUBigClusterMaxFreq", - "Duration": 0, - "Value": "1497600" - } - ] -} \ No newline at end of file + "Nodes": [ + { + "Name": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Path": "/sys/kernel/msm_performance/parameters/cpu_min_freq", + "Values": [ + "0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0", + "0:691200 1:691200 2:691200 3:691200 4:691200 5:691200 6:691200 7:691200", + "0:1497600 1:1497600 2:1497600 3:1497600 4:1497600 5:1497600 6:1497600 7:1497600", + "0:1958400 1:1958400 2:1958400 3:1958400 4:1497600 5:1497600 6:1497600 7:1497600", + "0:1958400 1:1958400 2:1958400 3:1958400 4:1900800 5:1900800 6:1900800 7:1900800", + "4:960000 5:960000 6:960000 7:960000" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Path": "/sys/kernel/msm_performance/parameters/cpu_max_freq", + "Values": [ + "0:9999999 1:9999999 2:9999999 3:9999999 4:9999999 5:9999999 6:9999999 7:9999999", + "0:1324800 1:1324800 2:1324800 3:1324800 4:1190400 5:1190400 6:1190400 7:1190400", + "0:1958400 1:1958400 2:1958400 3:1958400 4:1900800 5:1900800 6:1900800 7:1900800", + "0:1958400 1:1958400 2:1958400 3:1958400 4:2400000 5:2400000 6:2400000 7:2400000", + "4:1497600 5:1497600 6:1497600 7:1497600" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysClassKgslKgsl3d0DevfreqMinFreq", + "Path": "/sys/class/kgsl/kgsl-3d0/devfreq/min_freq", + "Values": [ + "295000000", + "345000000" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysClassKgslKgsl3d0DevfreqMaxFreq", + "Path": "/sys/class/kgsl/kgsl-3d0/devfreq/max_freq", + "Values": [ + "940000000", + "500000000" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedBoost", + "Path": "/proc/sys/walt/sched_boost", + "Values": [ + "0", + "1", + "2" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedBusyHysteresisEnableCpus", + "Path": "/proc/sys/walt/sched_busy_hysteresis_enable_cpus", + "Values": [ + "0", + "240" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedBusyHystNs", + "Path": "/proc/sys/walt/sched_busy_hyst_ns", + "Values": [ + "0", + "39" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuCpu0CoreCtlMinCpus", + "Path": "/sys/devices/system/cpu/cpu0/core_ctl/min_cpus", + "Values": [ + "1", + "3", + "4" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedMinTaskUtilForColocation", + "Path": "/proc/sys/walt/sched_min_task_util_for_colocation", + "Values": [ + "35", + "0" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedMinTaskUtilForBoost", + "Path": "/proc/sys/walt/sched_min_task_util_for_boost", + "Values": [ + "51", + "0" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuCpufreqPolicy4WaltBoost", + "Path": "/sys/devices/system/cpu/cpufreq/policy4/walt/boost", + "Values": [ + "-6", + "691200" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuCpufreqPolicy0WaltBoost", + "Path": "/sys/devices/system/cpu/cpufreq/policy0/walt/boost", + "Values": [ + "0", + "691200" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedFreq", + "Path": "/sys/devices/system/cpu/cpu4/cpufreq/walt/hispeed_freq", + "Values": [ + "1190000", + "940" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedLoad", + "Path": "/sys/devices/system/cpu/cpu4/cpufreq/walt/hispeed_load", + "Values": [ + "85", + "95" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrSampleMs", + "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/sample_ms", + "Values": [ + "4", + "33" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedUpmigrate", + "Path": "/proc/sys/walt/sched_upmigrate", + "Values": [ + "71", + "35", + "45" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedDownmigrate", + "Path": "/proc/sys/walt/sched_downmigrate", + "Values": [ + "65", + "30", + "40" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedGroupUpmigrate", + "Path": "/proc/sys/walt/sched_group_upmigrate", + "Values": [ + "100", + "35", + "45" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "procSysWaltSchedGroupDownmigrate", + "Path": "/proc/sys/walt/sched_group_downmigrate", + "Values": [ + "85", + "30", + "40" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrAbScale", + "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/ab_scale", + "Values": [ + "100", + "50" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrIoPercent", + "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/io_percent", + "Values": [ + "68", + "100" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrMinFreq", + "Path": "/sys/devices/system/cpu/bus_dcvs/DDR/19091000.qcom,bwmon-ddr/min_freq", + "Values": [ + "547000", + "1555000" + ], + "DefaultIndex": 0, + "ResetOnInit": true + }, + { + "Name": "devCpuDmaLatency", + "Path": "/dev/cpu_dma_latency", + "Values": [ + "2000000000", + "1" + ], + "HoldFd": true + } + ], + "Actions": [ + { + "PowerHint": "SUSTAINED_PERFORMANCE", + "Node": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Value": "0:691200 1:691200 2:691200 3:691200 4:691200 5:691200 6:691200 7:691200", + "Duration": 0 + }, + { + "PowerHint": "SUSTAINED_PERFORMANCE", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "0:1324800 1:1324800 2:1324800 3:1324800 4:1190400 5:1190400 6:1190400 7:1190400", + "Duration": 0 + }, + { + "PowerHint": "SUSTAINED_PERFORMANCE", + "Node": "sysClassKgslKgsl3d0DevfreqMinFreq", + "Value": "345000000", + "Duration": 0 + }, + { + "PowerHint": "SUSTAINED_PERFORMANCE", + "Node": "sysClassKgslKgsl3d0DevfreqMaxFreq", + "Value": "500000000", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Value": "0:1958400 1:1958400 2:1958400 3:1958400 4:1497600 5:1497600 6:1497600 7:1497600", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "procSysWaltSchedBoost", + "Value": "2", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "procSysWaltSchedBusyHysteresisEnableCpus", + "Value": "240", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "procSysWaltSchedBusyHystNs", + "Value": "39", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "sysDevicesSystemCpuCpu0CoreCtlMinCpus", + "Value": "3", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "procSysWaltSchedMinTaskUtilForColocation", + "Value": "0", + "Duration": 0 + }, + { + "PowerHint": "INTERACTION", + "Node": "procSysWaltSchedMinTaskUtilForBoost", + "Value": "0", + "Duration": 0 + }, + { + "PowerHint": "LAUNCH", + "Node": "procSysWaltSchedBoost", + "Value": "1", + "Duration": 2000 + }, + { + "PowerHint": "LAUNCH", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "0:1958400 1:1958400 2:1958400 3:1958400 4:2400000 5:2400000 6:2400000 7:2400000", + "Duration": 2000 + }, + { + "PowerHint": "LAUNCH", + "Node": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Value": "0:1497600 1:1497600 2:1497600 3:1497600 4:1497600 5:1497600 6:1497600 7:1497600", + "Duration": 2000 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuCpufreqPolicy4WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuCpufreqPolicy0WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedFreq", + "Value": "940", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedLoad", + "Value": "95", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrSampleMs", + "Value": "33", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "4:1497600 5:1497600 6:1497600 7:1497600", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "procSysWaltSchedUpmigrate", + "Value": "45", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "procSysWaltSchedDownmigrate", + "Value": "40", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "procSysWaltSchedGroupUpmigrate", + "Value": "45", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "procSysWaltSchedGroupDownmigrate", + "Value": "40", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuCpu0CoreCtlMinCpus", + "Value": "4", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrAbScale", + "Value": "50", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_LOW", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrIoPercent", + "Value": "100", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuCpufreqPolicy4WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuCpufreqPolicy0WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedFreq", + "Value": "940", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuCpu4CpufreqWaltHispeedLoad", + "Value": "95", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrSampleMs", + "Value": "33", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Value": "4:960000 5:960000 6:960000 7:960000", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "4:1497600 5:1497600 6:1497600 7:1497600", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "procSysWaltSchedUpmigrate", + "Value": "45", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "procSysWaltSchedDownmigrate", + "Value": "40", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "procSysWaltSchedGroupUpmigrate", + "Value": "45", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "procSysWaltSchedGroupDownmigrate", + "Value": "40", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuCpu0CoreCtlMinCpus", + "Value": "4", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrAbScale", + "Value": "50", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_MID", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrIoPercent", + "Value": "100", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuCpufreqPolicy4WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuCpufreqPolicy0WaltBoost", + "Value": "691200", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrSampleMs", + "Value": "33", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "4:1497600 5:1497600 6:1497600 7:1497600", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "procSysWaltSchedUpmigrate", + "Value": "35", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "procSysWaltSchedDownmigrate", + "Value": "30", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "procSysWaltSchedGroupUpmigrate", + "Value": "35", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "procSysWaltSchedGroupDownmigrate", + "Value": "30", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuCpu0CoreCtlMinCpus", + "Value": "4", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrAbScale", + "Value": "50", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrIoPercent", + "Value": "100", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_STREAMING_HIGH", + "Node": "sysDevicesSystemCpuBusDcvsDdr19091000QcomBwmonDdrMinFreq", + "Value": "1555000", + "Duration": 0 + }, + { + "PowerHint": "CAMERA_LAUNCH", + "Node": "devCpuDmaLatency", + "Value": "1", + "Duration": 1000 + }, + { + "PowerHint": "CAMERA_LAUNCH", + "Node": "procSysWaltSchedBoost", + "Value": "1", + "Duration": 1000 + }, + { + "PowerHint": "CAMERA_LAUNCH", + "Node": "sysKernelMsmPerformanceParametersCpuMaxFreq", + "Value": "0:1958400 1:1958400 2:1958400 3:1958400 4:1900800 5:1900800 6:1900800 7:1900800", + "Duration": 1000 + }, + { + "PowerHint": "CAMERA_LAUNCH", + "Node": "sysKernelMsmPerformanceParametersCpuMinFreq", + "Value": "0:1958400 1:1958400 2:1958400 3:1958400 4:1900800 5:1900800 6:1900800 7:1900800", + "Duration": 1000 + } + ] +>>>>>>> d16affa (garnet: Import qcom perf boosts into powerhint.json) +} diff --git a/configs/wifi/WCNSS_qcom_cfg.ini b/configs/wifi/WCNSS_qcom_cfg.ini index d8f4a9af..e646fcc4 100644 --- a/configs/wifi/WCNSS_qcom_cfg.ini +++ b/configs/wifi/WCNSS_qcom_cfg.ini @@ -223,8 +223,8 @@ gMaxMediumTime = 6000 # 802.11K support gRrmEnable=1 -#Enable Power Save offload -gEnablePowerSaveOffload=2 +# Enable Power Save Mode +gOptimizedPowerManagement=1 #Enable firmware uart print gEnablefwprint=0 diff --git a/device.mk b/device.mk index cb581cb2..176d1111 100644 --- a/device.mk +++ b/device.mk @@ -33,6 +33,9 @@ PRODUCT_PACKAGES += \ checkpoint_gc \ otapreopt_script +# Sign Keys +-include vendor/lineage-priv/keys/keys.mk + # API BOARD_SHIPPING_API_LEVEL := 31 PRODUCT_SHIPPING_API_LEVEL := $(BOARD_SHIPPING_API_LEVEL) @@ -142,10 +145,22 @@ PRODUCT_PACKAGES += \ PRODUCT_COPY_FILES += \ hardware/qcom-caf/sm8450/display/config/snapdragon_color_libs_config.xml:$(TARGET_COPY_OUT_VENDOR)/etc/snapdragon_color_libs_config.xml +# Dolby +PRODUCT_PACKAGES += \ + XiaomiDolby + # DRM PRODUCT_PACKAGES += \ android.hardware.drm-service.clearkey +# Device-specific settings +PRODUCT_PACKAGES += \ + XiaomiParts + +# EUICC (ESIM Support) +PRODUCT_PACKAGES += \ + EuiccGarnet + # Dynamic partitions PRODUCT_USE_DYNAMIC_PARTITIONS := true @@ -218,7 +233,7 @@ PRODUCT_COPY_FILES += \ # Keylayout PRODUCT_COPY_FILES += \ - $(LOCAL_PATH)/configs/keylayout/parrot-qrd-snd-card_Button_Jack.kl:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/usr/keylayout/parrot-qrd-snd-card_Button_Jack.kl + $(LOCAL_PATH)/configs/keylayout/parrot-qrd-snd-card_Button_Jack.kl:$(TARGET_COPY_OUT_VENDOR)/usr/keylayout/parrot-qrd-snd-card_Button_Jack.kl # Keymint PRODUCT_COPY_FILES += \ @@ -237,9 +252,6 @@ PRODUCT_COPY_FILES += \ $(AUDIO_HAL_DIR)/configs/common/codec2/service/1.0/c2audio.vendor.ext-arm.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/c2audio.vendor.ext-arm.policy \ $(AUDIO_HAL_DIR)/configs/common/codec2/service/1.0/c2audio.vendor.ext-arm64.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/c2audio.vendor.ext-arm64.policy \ -PRODUCT_COPY_FILES += \ - $(AUDIO_HAL_DIR)/configs/common/codec2/media_codecs_c2_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_c2_audio.xml - # Memtrack PRODUCT_PACKAGES += \ vendor.qti.hardware.memtrack-service @@ -459,3 +471,6 @@ PRODUCT_COPY_FILES += \ # Vendor $(call inherit-product, vendor/xiaomi/garnet/garnet-vendor.mk) + +# Vendor MiuiCamera +$(call inherit-product-if-exists, device/xiaomi/miuicamera-garnet/device.mk) diff --git a/extract-files.py b/extract-files.py index 589ca257..26fe0d6e 100755 --- a/extract-files.py +++ b/extract-files.py @@ -55,7 +55,18 @@ def lib_fixup_vendor_suffix(lib: str, partition: str, *args, **kwargs): .replace_needed( 'libgrpc++_unsecure.so', 'libgrpc++_unsecure_prebuilt.so' + 'system_ext/framework/mirilhook.jar': blob_fixup() + .apktool_patch('blob-patches/mirilhook.patch', '-r'), ), + ( + 'vendor/bin/hw/vendor.dolby.hardware.dms@2.0-service', + 'vendor/bin/hw/vendor.dolby.media.c2@1.0-service', + 'vendor/lib64/libdlbdsservice.so', + 'vendor/lib64/libdlbpreg.so', + 'vendor/lib64/soundfx/libdlbvol.so', + 'vendor/lib64/soundfx/libhwdap.so' , + ): blob_fixup() + .add_needed('libstagefright_foundation-v33.so'), ( 'vendor/etc/camera/pureView_parameter.xml', 'vendor/etc/camera/pureShot_parameter.xml' @@ -65,7 +76,7 @@ def lib_fixup_vendor_suffix(lib: str, partition: str, *args, **kwargs): .regex_replace('.+media_codecs_(google_audio|google_c2|google_telephony|vendor_audio).+\n', '') .regex_replace(r' true + + + 3.1 diff --git a/overlay/LineageSDKOverlayGarnet/res/values/config.xml b/overlay/LineageSDKOverlayGarnet/res/values/config.xml index 0ecc6fd5..50e66a47 100644 --- a/overlay/LineageSDKOverlayGarnet/res/values/config.xml +++ b/overlay/LineageSDKOverlayGarnet/res/values/config.xml @@ -8,7 +8,7 @@ restart restart_recovery - restart_fastboot + restart_systemui restart_bootloader diff --git a/overlay/WifiOverlayGarnet/res/values/config.xml b/overlay/WifiOverlayGarnet/res/values/config.xml index c99ad189..61f6d7e2 100644 --- a/overlay/WifiOverlayGarnet/res/values/config.xml +++ b/overlay/WifiOverlayGarnet/res/values/config.xml @@ -10,6 +10,9 @@ is no longer indicative, and a separate config now exists for each band --> true + + true + false diff --git a/parts/Android.bp b/parts/Android.bp new file mode 100644 index 00000000..257c8cb2 --- /dev/null +++ b/parts/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2017-2020 The LineageOS Project +// +// SPDX-License-Identifier: Apache-2.0 +// + +android_app { + name: "XiaomiParts", + + srcs: ["src/**/*.java"], + + certificate: "platform", + platform_apis: true, + system_ext_specific: true, + privileged: true, + + static_libs: [ + "org.lineageos.settings.resources", + ], + + 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, +} diff --git a/parts/AndroidManifest.xml b/parts/AndroidManifest.xml new file mode 100644 index 00000000..4aa95f8d --- /dev/null +++ b/parts/AndroidManifest.xml @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parts/permissions/privapp_whitelist_org.lineageos.settings.xml b/parts/permissions/privapp_whitelist_org.lineageos.settings.xml new file mode 100644 index 00000000..b98a0ae3 --- /dev/null +++ b/parts/permissions/privapp_whitelist_org.lineageos.settings.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/parts/res/color-night/app_name_text_selector.xml b/parts/res/color-night/app_name_text_selector.xml new file mode 100644 index 00000000..8cddd23b --- /dev/null +++ b/parts/res/color-night/app_name_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color-night/app_package_text_selector.xml b/parts/res/color-night/app_package_text_selector.xml new file mode 100644 index 00000000..d0974635 --- /dev/null +++ b/parts/res/color-night/app_package_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color-night/button_bg_selector.xml b/parts/res/color-night/button_bg_selector.xml new file mode 100644 index 00000000..2b3d506b --- /dev/null +++ b/parts/res/color-night/button_bg_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color-night/button_text_selector.xml b/parts/res/color-night/button_text_selector.xml new file mode 100644 index 00000000..8cddd23b --- /dev/null +++ b/parts/res/color-night/button_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color-night/search_bg_selector.xml b/parts/res/color-night/search_bg_selector.xml new file mode 100644 index 00000000..2b3d506b --- /dev/null +++ b/parts/res/color-night/search_bg_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color/app_name_text_selector.xml b/parts/res/color/app_name_text_selector.xml new file mode 100644 index 00000000..79324ffa --- /dev/null +++ b/parts/res/color/app_name_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color/app_package_text_selector.xml b/parts/res/color/app_package_text_selector.xml new file mode 100644 index 00000000..29fc19c3 --- /dev/null +++ b/parts/res/color/app_package_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color/button_bg_selector.xml b/parts/res/color/button_bg_selector.xml new file mode 100644 index 00000000..10567c53 --- /dev/null +++ b/parts/res/color/button_bg_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color/button_text_selector.xml b/parts/res/color/button_text_selector.xml new file mode 100644 index 00000000..2b3d506b --- /dev/null +++ b/parts/res/color/button_text_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/color/search_bg_selector.xml b/parts/res/color/search_bg_selector.xml new file mode 100644 index 00000000..3cca1976 --- /dev/null +++ b/parts/res/color/search_bg_selector.xml @@ -0,0 +1,4 @@ + + + + diff --git a/parts/res/drawable-nodpi/image_preview1.jpg b/parts/res/drawable-nodpi/image_preview1.jpg new file mode 100644 index 00000000..71bcb7e5 Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview1.jpg differ diff --git a/parts/res/drawable-nodpi/image_preview1.webp b/parts/res/drawable-nodpi/image_preview1.webp new file mode 100644 index 00000000..0e703f0b Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview1.webp differ diff --git a/parts/res/drawable-nodpi/image_preview2.jpg b/parts/res/drawable-nodpi/image_preview2.jpg new file mode 100644 index 00000000..4153d8e7 Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview2.jpg differ diff --git a/parts/res/drawable-nodpi/image_preview2.webp b/parts/res/drawable-nodpi/image_preview2.webp new file mode 100644 index 00000000..87cf1e47 Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview2.webp differ diff --git a/parts/res/drawable-nodpi/image_preview3.jpg b/parts/res/drawable-nodpi/image_preview3.jpg new file mode 100644 index 00000000..ab83f08c Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview3.jpg differ diff --git a/parts/res/drawable-nodpi/image_preview3.webp b/parts/res/drawable-nodpi/image_preview3.webp new file mode 100644 index 00000000..b07ff858 Binary files /dev/null and b/parts/res/drawable-nodpi/image_preview3.webp differ diff --git a/parts/res/drawable/bg_button_rounded.xml b/parts/res/drawable/bg_button_rounded.xml new file mode 100644 index 00000000..62a16f04 --- /dev/null +++ b/parts/res/drawable/bg_button_rounded.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/parts/res/drawable/bg_round_icon.xml b/parts/res/drawable/bg_round_icon.xml new file mode 100644 index 00000000..b6e895ea --- /dev/null +++ b/parts/res/drawable/bg_round_icon.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/parts/res/drawable/bg_search_rounded.xml b/parts/res/drawable/bg_search_rounded.xml new file mode 100644 index 00000000..71f0f62e --- /dev/null +++ b/parts/res/drawable/bg_search_rounded.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/parts/res/drawable/ic_auto_hbm_tile.xml b/parts/res/drawable/ic_auto_hbm_tile.xml new file mode 100644 index 00000000..bcab34db --- /dev/null +++ b/parts/res/drawable/ic_auto_hbm_tile.xml @@ -0,0 +1,11 @@ + + + + diff --git a/parts/res/drawable/ic_clear_speaker.xml b/parts/res/drawable/ic_clear_speaker.xml new file mode 100644 index 00000000..4e2981d7 --- /dev/null +++ b/parts/res/drawable/ic_clear_speaker.xml @@ -0,0 +1,13 @@ + + + + diff --git a/parts/res/drawable/ic_custom_seekbar_minus.xml b/parts/res/drawable/ic_custom_seekbar_minus.xml new file mode 100644 index 00000000..5cb45db0 --- /dev/null +++ b/parts/res/drawable/ic_custom_seekbar_minus.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/parts/res/drawable/ic_custom_seekbar_plus.xml b/parts/res/drawable/ic_custom_seekbar_plus.xml new file mode 100644 index 00000000..4646c778 --- /dev/null +++ b/parts/res/drawable/ic_custom_seekbar_plus.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/parts/res/drawable/ic_custom_seekbar_reset.xml b/parts/res/drawable/ic_custom_seekbar_reset.xml new file mode 100644 index 00000000..662a13c1 --- /dev/null +++ b/parts/res/drawable/ic_custom_seekbar_reset.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/parts/res/drawable/ic_gamebar.xml b/parts/res/drawable/ic_gamebar.xml new file mode 100644 index 00000000..54f24ae5 --- /dev/null +++ b/parts/res/drawable/ic_gamebar.xml @@ -0,0 +1,11 @@ + + + + diff --git a/parts/res/drawable/ic_hbm_tile.xml b/parts/res/drawable/ic_hbm_tile.xml new file mode 100644 index 00000000..fdf1820d --- /dev/null +++ b/parts/res/drawable/ic_hbm_tile.xml @@ -0,0 +1,10 @@ + + + diff --git a/parts/res/drawable/ic_image_preview_arrow_left.xml b/parts/res/drawable/ic_image_preview_arrow_left.xml new file mode 100644 index 00000000..933561ac --- /dev/null +++ b/parts/res/drawable/ic_image_preview_arrow_left.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/parts/res/drawable/ic_image_preview_arrow_right.xml b/parts/res/drawable/ic_image_preview_arrow_right.xml new file mode 100644 index 00000000..525eb54b --- /dev/null +++ b/parts/res/drawable/ic_image_preview_arrow_right.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/parts/res/drawable/ic_image_preview_page_indicator_focused.xml b/parts/res/drawable/ic_image_preview_page_indicator_focused.xml new file mode 100644 index 00000000..1f7d3d45 --- /dev/null +++ b/parts/res/drawable/ic_image_preview_page_indicator_focused.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/parts/res/drawable/ic_image_preview_page_indicator_unfocused.xml b/parts/res/drawable/ic_image_preview_page_indicator_unfocused.xml new file mode 100644 index 00000000..a50f9c6f --- /dev/null +++ b/parts/res/drawable/ic_image_preview_page_indicator_unfocused.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/parts/res/drawable/ic_refresh_120.xml b/parts/res/drawable/ic_refresh_120.xml new file mode 100644 index 00000000..f81418b2 --- /dev/null +++ b/parts/res/drawable/ic_refresh_120.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_refresh_60.xml b/parts/res/drawable/ic_refresh_60.xml new file mode 100644 index 00000000..2c4a62fe --- /dev/null +++ b/parts/res/drawable/ic_refresh_60.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_refresh_90.xml b/parts/res/drawable/ic_refresh_90.xml new file mode 100644 index 00000000..d59fba19 --- /dev/null +++ b/parts/res/drawable/ic_refresh_90.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_refresh_default.xml b/parts/res/drawable/ic_refresh_default.xml new file mode 100644 index 00000000..a4b7d3a1 --- /dev/null +++ b/parts/res/drawable/ic_refresh_default.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/parts/res/drawable/ic_saturation_tile.xml b/parts/res/drawable/ic_saturation_tile.xml new file mode 100644 index 00000000..32c0fa63 --- /dev/null +++ b/parts/res/drawable/ic_saturation_tile.xml @@ -0,0 +1,11 @@ + + + + diff --git a/parts/res/drawable/ic_scenes.xml b/parts/res/drawable/ic_scenes.xml new file mode 100644 index 00000000..1eee4f1e --- /dev/null +++ b/parts/res/drawable/ic_scenes.xml @@ -0,0 +1,9 @@ + + + + diff --git a/parts/res/drawable/ic_thermal_benchmark.xml b/parts/res/drawable/ic_thermal_benchmark.xml new file mode 100644 index 00000000..8b3f2c12 --- /dev/null +++ b/parts/res/drawable/ic_thermal_benchmark.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_browser.xml b/parts/res/drawable/ic_thermal_browser.xml new file mode 100644 index 00000000..73880a00 --- /dev/null +++ b/parts/res/drawable/ic_thermal_browser.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/parts/res/drawable/ic_thermal_camera.xml b/parts/res/drawable/ic_thermal_camera.xml new file mode 100644 index 00000000..642c0823 --- /dev/null +++ b/parts/res/drawable/ic_thermal_camera.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_default.xml b/parts/res/drawable/ic_thermal_default.xml new file mode 100644 index 00000000..cc78c361 --- /dev/null +++ b/parts/res/drawable/ic_thermal_default.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_dialer.xml b/parts/res/drawable/ic_thermal_dialer.xml new file mode 100644 index 00000000..f87e39b9 --- /dev/null +++ b/parts/res/drawable/ic_thermal_dialer.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_gaming.xml b/parts/res/drawable/ic_thermal_gaming.xml new file mode 100644 index 00000000..29a13b8f --- /dev/null +++ b/parts/res/drawable/ic_thermal_gaming.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_streaming.xml b/parts/res/drawable/ic_thermal_streaming.xml new file mode 100644 index 00000000..e6e272ba --- /dev/null +++ b/parts/res/drawable/ic_thermal_streaming.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/parts/res/drawable/ic_thermal_tile.xml b/parts/res/drawable/ic_thermal_tile.xml new file mode 100644 index 00000000..ccdbc7a5 --- /dev/null +++ b/parts/res/drawable/ic_thermal_tile.xml @@ -0,0 +1,11 @@ + + + diff --git a/parts/res/layout/activity_game_bar.xml b/parts/res/layout/activity_game_bar.xml new file mode 100644 index 00000000..77f32cd3 --- /dev/null +++ b/parts/res/layout/activity_game_bar.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/parts/res/layout/activity_game_bar_app_selector.xml b/parts/res/layout/activity_game_bar_app_selector.xml new file mode 100644 index 00000000..c633af95 --- /dev/null +++ b/parts/res/layout/activity_game_bar_app_selector.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/parts/res/layout/activity_sound_control.xml b/parts/res/layout/activity_sound_control.xml new file mode 100644 index 00000000..eec564ce --- /dev/null +++ b/parts/res/layout/activity_sound_control.xml @@ -0,0 +1,5 @@ + + diff --git a/parts/res/layout/game_bar.xml b/parts/res/layout/game_bar.xml new file mode 100644 index 00000000..e8948d9c --- /dev/null +++ b/parts/res/layout/game_bar.xml @@ -0,0 +1,10 @@ + + + diff --git a/parts/res/layout/image_layout.xml b/parts/res/layout/image_layout.xml new file mode 100644 index 00000000..12836e4c --- /dev/null +++ b/parts/res/layout/image_layout.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/parts/res/layout/image_preview_layout.xml b/parts/res/layout/image_preview_layout.xml new file mode 100644 index 00000000..5aa721ae --- /dev/null +++ b/parts/res/layout/image_preview_layout.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parts/res/layout/preference_custom_seekbar.xml b/parts/res/layout/preference_custom_seekbar.xml new file mode 100644 index 00000000..6532fba5 --- /dev/null +++ b/parts/res/layout/preference_custom_seekbar.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parts/res/layout/refresh_layout.xml b/parts/res/layout/refresh_layout.xml new file mode 100644 index 00000000..6467efae --- /dev/null +++ b/parts/res/layout/refresh_layout.xml @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/parts/res/layout/refresh_list_item.xml b/parts/res/layout/refresh_list_item.xml new file mode 100644 index 00000000..ff7a2e80 --- /dev/null +++ b/parts/res/layout/refresh_list_item.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + diff --git a/parts/res/layout/thermal_layout.xml b/parts/res/layout/thermal_layout.xml new file mode 100644 index 00000000..3982a35e --- /dev/null +++ b/parts/res/layout/thermal_layout.xml @@ -0,0 +1,18 @@ + + + diff --git a/parts/res/layout/thermal_list_item.xml b/parts/res/layout/thermal_list_item.xml new file mode 100644 index 00000000..864b7d9b --- /dev/null +++ b/parts/res/layout/thermal_list_item.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + diff --git a/parts/res/menu/auto_hbm_menu.xml b/parts/res/menu/auto_hbm_menu.xml new file mode 100644 index 00000000..dee9682b --- /dev/null +++ b/parts/res/menu/auto_hbm_menu.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/parts/res/menu/saturation_menu.xml b/parts/res/menu/saturation_menu.xml new file mode 100644 index 00000000..2f1841c7 --- /dev/null +++ b/parts/res/menu/saturation_menu.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/parts/res/raw/clear_speaker_sound.mp3 b/parts/res/raw/clear_speaker_sound.mp3 new file mode 100644 index 00000000..a67b1351 Binary files /dev/null and b/parts/res/raw/clear_speaker_sound.mp3 differ diff --git a/parts/res/values-es-rES/strings.xml b/parts/res/values-es-rES/strings.xml new file mode 100644 index 00000000..0e458804 --- /dev/null +++ b/parts/res/values-es-rES/strings.xml @@ -0,0 +1,122 @@ + + + + + + Ajustes Xiaomi + Agregar mosaico + Mosaico agregado + Mosaico no agregado + Mosaico ya agregado + Activado + Desactivado + + + Perfiles térmicos + Ajusta perfiles térmicos por aplicación para un rendimiento óptimo + Sin optimización + Rendimiento + Navegador + Cámara + Teléfono + Juegos + Transmisión + + + Frecuencia de actualización por aplicación + Establece la frecuencia de actualización máxima para una aplicación específica + Predeterminado + 60 Hz + 90 Hz + 120 Hz + + + Limpiar altavoz + Reproducir un audio de 30 segundos para limpiar el altavoz + Ejecuta esta función una o dos veces si observas que tu altavoz está ligeramente obstruido por polvo. Ajusta el volumen multimedia al máximo.\n\nSi el altavoz está muy obstruido, ejecuta esta función 2-5 veces mientras sacudes el dispositivo con el altavoz orientado hacia abajo. + + + Saturación de pantalla + Controla el nivel de saturación de la pantalla + Cambiar el modo de color en los ajustes de pantalla anulará el nivel de saturación, por lo que será necesario volver a aplicarlo. + + + Vista previa + Siguiente + Anterior + + + Valor: %s + por defecto + Valor predeterminado: %s\nMantén pulsado para establecer + El valor predeterminado está establecido + + + Modo de alta luminosidad automático (HBM) + Usar HBM automático + HBM automático + Activado + Desactivado + HBM + Modo alta luminosidad no disponible + El modo de alta luminosidad automático o el brillo automático están activados + Modo de alta luminosidad + El modo de alta luminosidad está activado. El uso prolongado puede dañar la pantalla. + Activa automáticamente el modo de alta luminosidad según la intensidad de la luz solar (lux) + Umbral de luz + Define el umbral de intensidad de luz en unidades lux al que se activa el modo de alta luminosidad + Tiempo de activación + Duración en segundos que el umbral de luz debe mantenerse continuamente para activar el modo de alta luminosidad + Tiempo de desactivación + Duración en segundos durante la cual el modo de alta luminosidad se desactiva si el umbral de luz cae por debajo del nivel establecido + + + GameBar + Activa la superposición del sistema (FPS, Temp, etc.) + Se requiere permiso para mostrar sobre otras aplicaciones + Permiso de superposición concedido + Permiso de superposición denegado + GameBar + Alternar la superposición de juego + + + Carga directa + Habilita la carga directa, omitiendo la batería + Aviso: la carga directa puede aumentar la temperatura del dispositivo o dañar la batería. Úsalo con precaución. + La carga directa no es compatible con este dispositivo + + + Control de núcleos + Activa o desactiva manualmente núcleos de CPU individuales + Control avanzado de CPU. Apagar núcleos puede ahorrar batería o reducir la carga térmica, pero puede afectar el rendimiento y la estabilidad.\n\nAl menos dos núcleos pequeños deben permanecer en línea en todo momento. + Núcleo 0 (pequeño) + Núcleo 1 (pequeño) + Núcleo 2 (pequeño) + Núcleo 3 (pequeño) + Núcleo 4 (grande) + Núcleo 5 (grande) + Núcleo 6 (grande) + Núcleo 7 (grande) + + + Control de sonido + Ganancia del micrófono (dB) + Ganancia auriculares izquierdo + Ganancia auriculares derecho + Habilitar control de sonido + + diff --git a/parts/res/values-in-rID/strings.xml b/parts/res/values-in-rID/strings.xml new file mode 100644 index 00000000..1e29f9d2 --- /dev/null +++ b/parts/res/values-in-rID/strings.xml @@ -0,0 +1,122 @@ + + + + + + Setelan Xiaomi + Tambahkan tile + Tile ditambahkan + Tile tidak ditambahkan + Tile sudah ditambahkan + Aktif + Non-aktif + + + Profil Thermal + Atur profil thermal per-aplikasi untuk performa optimal + Tanpa optimalisasi + Performa + Browser + Kamera + Panggilan + Gaming + Streaming + + + Refresh rate per-aplikasi + Atur maksimum refresh rate untuk aplikasi tertentu + Bawaan + 60 Hz + 90 Hz + 120 Hz + + + Bersihkan speaker + Memutar suara 30 detik untuk membersihkan speaker + Gunakan fitur ini sekali atau dua kali jika speaker tertutup debu. Atur volume ke maksimal.\n\nJikas Speaker tertutup penuh, gunakan fitur ini 2-5 kali sambil "mengocok" perangkatmu dengan speaker mengarah kebawah + + + Saturasi Tampilan + Atur level saturasi dari tamplan + Mengubah mode warna dari pengaturan tampilan akan menimpa level saturasi, butuh diterapkan ulang + + + Preview + Lanjut + Kembali + + + Value: %s + secara bawaan + Nilai bawaan: %s\nTahan untuk menerapkan + Nilai bawaan ditetapkan + + + Mode Kecerahan Tinggi otomatis (HBM) + Hidupkan HBM otomatis + HBM Otomatis + Diaktifkan + Dinonaktifkan + HBM + HBM Tidak Tersedia + HBM Otomatis atau Kecerahan Otomatis diaktifkan + Mode Kecerahan Tinggi + Mode Kecerahan Tinggi diaktifkan. Penggunaan terlalu lama mungkin merusak layar. + Hidupkan otomatis mode kecerahan tinggi berdasarkan intensitas cahaya (lux) + Batas Cahaya + Tentukan batas intensitas cahaya pada saat nilai lux berapa HBM aktif + Waktu aktif + Durasi dalam detik berapa batas waktu harus terus dipenuhi untuk mengaktifkan HBM + Waktu nonaktif + Durasi dalam detik berapa HBM dinonaktifkan jika intensitas cahaya jatuh dibawah level batas yang ditentukan + + + GameBar + Mengaktifkan overlay sistem (FPS, Temp, dll.) + Izin overlay dibutuhkan + Izin overlay diberikan + Izin overlay ditolak + GameBar + Hidupkan overlay game + + + Bypass charging + Hidupkan pengisian daya langsung, tanpa menggunakan baterai + Peringatan: Bypass charging mungkin menyebabkan peningkatan suhu perangkat atau kerusakan baterai. Gunakan dengan hati-hati + Bypass charging tidak didukung pada perangkat ini + + + Core Control + Aktifkan atau nonaktifkan CPU core tertentu secara manual + CPU control lanjutan. Menonaktifkan core mungkin menghemat baterai atau mengurangi suhu, tapi mungkin mempengaruhi performa dan stabilitas. \n\nSetidaknya biarkan 2 core kecil harus tetap hidup setiap saat + Core 0 (Kecil) + Core 1 (Kecil) + Core 2 (Kecil) + Core 3 (Kecil) + Core 4 (Besar) + Core 5 (Besar) + Core 6 (Besar) + Core 7 (Besar) + + + Kontrol Suara + Microphone gain (dB) + Gain headphone kiri + Gain headphone kanan + Aktifkan kontrol suara + + diff --git a/parts/res/values-ru-rRU/strings.xml b/parts/res/values-ru-rRU/strings.xml new file mode 100644 index 00000000..da4a59f3 --- /dev/null +++ b/parts/res/values-ru-rRU/strings.xml @@ -0,0 +1,95 @@ + + + + Настройки Xiaomi + Добавить плитку + Плитка добавлена + Плитка не добавлена + Плитка уже добавлена + Вкл. + Выкл. + + Профили производительности + Настройка профилей производительности для каждого приложения + Без оптимизации + Производительность + Браузер + Камера + Телефон + Игры + Стриминг + + Частота обновления для приложений + Установка максимальной частоты обновления для каждого приложения + По умолчанию + 60 Гц + 90 Гц + 120 Гц + + Очистка динамика + Воспроизвести 30-секундный звук для очистки динамика + Используйте эту функцию один или два раза, если динамик незначительно забит пылью. Установите громкость мультимедиа на максимум.\n\nЕсли динамик сильно забит, запустите эту функцию 2-5 раз, встряхивая устройство динамиком вниз. + + Насыщенность дисплея + Управление уровнем насыщенности дисплея + Изменение цветового режима в настройках экрана отменит текущий уровень насыщенности, его потребуется применить заново. + + Предпросмотр + Далее + Назад + + Значение: %s + по умолчанию + Значение по умолчанию: %s\nДлительное нажатие для установки + Установлено значение по умолчанию + + Автоматический режим высокой яркости (HBM) + Использовать автоматический HBM + Авто-HBM + Включён + Выключен + HBM + HBM недоступен + Авто-HBM или автояркость включены + Режим высокой яркости + Режим высокой яркости активирован. Длительное использование может повредить дисплей. + Автоматически включать режим высокой яркости в зависимости от интенсивности солнечного света (в люксах) + Порог освещённости + Установите порог интенсивности света в люксах, при котором будет активирован HBM + Время включения + Продолжительность в секундах, в течение которой порог освещенности должен постоянно соблюдаться для активации HBM + Время выключения + Продолжительность в секундах, по истечении которой HBM будет деактивирован, если порог освещенности упадет ниже установленного уровня + + Игровая панель + Включить системный оверлей (FPS, температура и т.д.) + Требуется разрешение на отображение поверх других приложений + Разрешение на отображение поверх других приложений предоставлено + В разрешении на отображение поверх других приложений отказано + Игровая панель + Переключить игровой оверлей + + Прямое питание + Включить питание напрямую, в обход аккумулятора + Внимание: Прямое питание может привести к повышению температуры устройства или повреждению аккумулятора. Используйте с осторожностью. + Прямое питание не поддерживается на этом устройстве + + Управление ядрами + Ручное включение или отключение отдельных ядер ЦП + Расширенное управление ЦП. Отключение ядер может сэкономить заряд батареи или снизить тепловую нагрузку, но может повлиять на производительность и стабильность.\n\nНеобходимо, чтобы как минимум два малых ядра всегда оставались в сети. + Ядро 0 (малое) + Ядро 1 (малое) + Ядро 2 (малое) + Ядро 3 (малое) + Ядро 4 (большое) + Ядро 5 (большое) + Ядро 6 (большое) + Ядро 7 (большое) + + Управление звуком + Усиление микрофона (дБ) + Усиление левого наушника + Усиление правого наушника + Включить управление звуком + + \ No newline at end of file diff --git a/parts/res/values-tr-rTR/strings.xml b/parts/res/values-tr-rTR/strings.xml new file mode 100644 index 00000000..26ec6e0a --- /dev/null +++ b/parts/res/values-tr-rTR/strings.xml @@ -0,0 +1,106 @@ + + + + + Xiaomi Ayarları + Döşeme ekle + Döşeme eklendi + Döşeme eklenemedi + Döşeme zaten ekli + Açık + Kapalı + + + Termal profiller + Optimum performans için uygulamaya özel termal profilleri ayarla + Optimizasyon yok + Performans + Tarayıcı + Kamera + Çevirici + Oyun + Yayın Akışı + + + Uygulamaya özel yenileme hızı + Belirli bir uygulama için maksimum yenileme hızını ayarla + Varsayılan + 60 Hz + 90 Hz + 120 Hz + + + Hoparlör Temizleyici + Hoparlörü temizlemek için 30 saniyelik bir ses çal + Hoparlörünüzün hafifçe tozla tıkandığını düşünüyorsanız bu özelliği bir veya iki kez çalıştırın. Medya ses düzeyini maksimuma ayarlayın.\n\nHoparlör aşırı tıkalıysa, cihazınızı hoparlör aşağı bakacak şekilde sallarken bu özelliği 2-5 kez çalıştırın. + + + Ekran Doygunluğu + Ekranın doygunluk seviyesini kontrol et + Ekran ayarlarında renk modunu değiştirmek, doygunluk seviyesini geçersiz kılacak ve yeniden uygulanmasını gerektirecektir. + + + Önizleme + Sonraki + Önceki + + + Değer: %s + varsayılan olarak + Varsayılan değer: %s\nAyarlamak için uzun dokunun + Varsayılan değer ayarlandı + + + Otomatik Yüksek Parlaklık Modu (YPM) + Otomatik YPM kullan + Otomatik YPM + Etkin + Devre Dışı + YPM + YPM Kullanılamıyor + Otomatik YPM veya Otomatik Parlaklık etkinleştirildi + Yüksek Parlaklık Modu + Yüksek Parlaklık Modu şimdi etkinleştirildi. Uzun süreli kullanım ekranınıza zarar verebilir. + Güneş ışığı yoğunluğuna (lüks) göre yüksek parlaklık modunu otomatik olarak etkinleştir + Işık eşiği + YPM’nin etkinleştirileceği lüks birimindeki ışık yoğunluğu eşiğini tanımla + Etkinleştirme süresi + YPM’yi etkinleştirmek için ışık eşiğinin sürekli olarak karşılanması gereken süre (saniye) + Devre dışı bırakma süresi + Işık eşiği ayarlanan seviyenin altına düşerse YPM’nin devre dışı bırakılacağı süre (saniye) + + + Oyun Çubuğu + Sistem katmanını etkinleştir (FPS, Sıcaklık, vb.) + Katman izni gerekiyor + Katman izni verildi + Katman izni reddedildi + Oyun Çubuğu + Oyun katmanını aç/kapat + + + Şarjı baypas et + Bataryayı atlayarak doğrudan şarj etmeyi etkinleştir + Uyarı: Şarjı baypas etmek cihaz sıcaklığını artırabilir veya bataryaya zarar verebilir. Dikkatli kullanın. + Şarjı baypas etme bu cihazda desteklenmiyor + + + Çekirdek Kontrolü + Tek tek CPU çekirdeklerini manuel olarak etkinleştir veya devre dışı bırak + Gelişmiş CPU kontrolü. Çekirdekleri kapatmak pil veya termal yükten tasarruf sağlayabilir, ancak performansı ve kararlılığı etkileyebilir.\n\nEn az iki küçük çekirdeğin her zaman çevrimiçi kalması gerekir. + Çekirdek 0 (Küçük) + Çekirdek 1 (Küçük) + Çekirdek 2 (Küçük) + Çekirdek 3 (Küçük) + Çekirdek 4 (Büyük) + Çekirdek 5 (Büyük) + Çekirdek 6 (Büyük) + Çekirdek 7 (Büyük) + + + Ses Kontrolü + Mikrofon kazancı (dB) + Sol kulaklık kazancı + Sağ kulaklık kazancı + Ses kontrolünü etkinleştir + diff --git a/parts/res/values-zh-rCN/strings.xml b/parts/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..99cbc0bc --- /dev/null +++ b/parts/res/values-zh-rCN/strings.xml @@ -0,0 +1,122 @@ + + + + + + 小米设置 + 添加磁贴 + 磁贴已添加 + 磁贴添加失败 + 磁贴已存在 + + + + + 温度配置文件 + 调整应用温度配置文件以获得最佳性能 + 无优化 + 性能模式 + 浏览器 + 相机 + 拨号 + 游戏 + 流媒体 + + + 应用刷新率 + 为特定应用设置最大刷新率 + 默认 + 60 Hz + 90 Hz + 120 Hz + + + 扬声器清理 + 播放30秒音频以清理扬声器 + 如果您发现扬声器轻微被灰尘堵塞,请运行此功能一至两次。请将媒体音量调至最大。\n\n如果扬声器堵塞严重,请在运行此功能2-5次的同时,将设备扬声器朝下摇晃。 + + + 显示饱和度 + 控制显示屏的饱和度级别 + 在显示设置中更改色彩模式将覆盖饱和度级别,需要重新应用。 + + + 预览 + 下一页 + 上一页 + + + 数值: %s + 默认 + 默认值: %s\n长按以设置 + 默认值已设置 + + + 自动高亮度模式 (HBM) + 使用自动 HBM + 自动 HBM + 已启用 + 已禁用 + HBM + HBM 不可用 + 自动 HBM 或自动亮度已启用 + 高亮度模式 + 高亮度模式现已激活。长时间使用可能会损坏您的显示屏。 + 根据阳光强度 (lux) 自动启用高亮度模式 + 光线阈值 + 定义激活 HBM 的光线强度阈值 (lux 单位) + 启用时间 + 光线阈值持续满足以激活 HBM 所需的持续时间 (秒) + 禁用时间 + 当光线强度低于设定水平时,HBM 停用的持续时间 (秒) + + + 游戏工具栏 + 启用系统悬浮窗 (FPS, 温度等) + 需要悬浮窗权限 + 悬浮窗权限已授予 + 悬浮窗权限被拒绝 + 游戏工具栏 + 切换游戏悬浮窗 + + + 旁路充电 + 启用直接充电,绕过电池 + 警告:旁路充电可能导致设备温度升高或电池损坏。请谨慎使用。 + 此设备不支持旁路充电 + + + 核心控制 + 手动启用或禁用单个 CPU 核心 + 高级 CPU 控制。关闭核心可能节省电量或减轻热负载,但可能影响性能和稳定性。\n\n至少需要保持两个小核心始终在线。 + 核心 0 (小核) + 核心 1 (小核) + 核心 2 (小核) + 核心 3 (小核) + 核心 4 (大核) + 核心 5 (大核) + 核心 6 (大核) + 核心 7 (大核) + + + 声音控制 + 麦克风增益 (dB) + 左耳机增益 + 右耳机增益 + 启用声音控制 + + \ No newline at end of file diff --git a/parts/res/values/arrays.xml b/parts/res/values/arrays.xml new file mode 100644 index 00000000..222a13ff --- /dev/null +++ b/parts/res/values/arrays.xml @@ -0,0 +1,119 @@ + + + + + + New API (Default) + Legacy Sysfs + + + new + legacy + + + + + Every 500ms + Every second + Every 2 seconds + Every 5 seconds + + + 500 + 1000 + 2000 + 5000 + + + + + Top Left + Top Center + Top Right + Bottom Left + Bottom Center + Bottom Right + Custom Draggable + + + top_left + top_center + top_right + bottom_left + bottom_center + bottom_right + draggable + + + + + White + Crimson + Fruit Salad + Royal Blue + Amber + Teal + Electric Violet + Magenta + + + + #FFFFFF + #DC143C + #4CAF50 + #4169E1 + #FFBF00 + #008080 + #8A2BE2 + #FF1493 + + + + + Full + Minimal + + + full + minimal + + + + + Side-by-Side + Stacked + + + side_by_side + stacked + + + + + 1 second + 3 seconds + 5 seconds + 10 seconds + + + 1000 + 3000 + 5000 + 10000 + + + diff --git a/parts/res/values/attrs.xml b/parts/res/values/attrs.xml new file mode 100644 index 00000000..56d15f5b --- /dev/null +++ b/parts/res/values/attrs.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/parts/res/values/strings.xml b/parts/res/values/strings.xml new file mode 100644 index 00000000..53858d60 --- /dev/null +++ b/parts/res/values/strings.xml @@ -0,0 +1,100 @@ + + + + + + Xiaomi Settings + Add tile + Tile added + Tile not added + Tile already added + On + Off + + + Thermal profiles + Adjust per-app thermal profiles for optimum performance + No optimization + Performance + Browser + Camera + Dialer + Gaming + Streaming + + + Per-app refresh rate + Set the maximum refresh rate for a specific application + Default + 60 Hz + 90 Hz + 120 Hz + + + Clear speaker + Play a 30-second audio to clear the speaker + Run this feature once or twice if you find that your speaker is lightly blocked by dust. Set media volume to maximum.\n\nIf the speaker is blocked heavily, run this feature 2-5 times while shaking your device with the speaker facing downwards. + + + Display Saturation + Control the saturation level of the display + Changing the color mode in display settings will override the saturation level, requiring it to be reapplied. + + + Preview + Next + Previous + + + Value: %s + by default + Default value: %s\nLong tap to set + Default value is set + + + Automatic High Brightness Mode (HBM) + Use automatic HBM + Auto HBM + Enabled + Disabled + HBM + HBM Unavailable + Auto HBM or Auto Brightness is enabled + High Brightness Mode + High Brightness Mode is now activated. Long time usage may damage your display. + Automatically enable high brightness mode based on sunlight intensity (lux) + Light threshold + Define the light intensity threshold in lux units at which HBM is activated + Enable time + Duration in seconds that the light threshold must be continuously met to activate HBM + Disable time + Duration in seconds for which HBM is deactivated if the light threshold falls below the set level + + + GameBar + Enable the system overlay (FPS, Temp, etc.) + Overlay permission is required + Overlay permission granted + Overlay permission denied + GameBar + Toggle the game overlay + + + Bypass charging + Enable direct charging, bypassing battery + Warning: Bypass charging may cause increased device temperature or battery damage. Use with caution. + Bypass charging is not supported on this device diff --git a/parts/res/xml/auto_hbm.xml b/parts/res/xml/auto_hbm.xml new file mode 100644 index 00000000..eb660059 --- /dev/null +++ b/parts/res/xml/auto_hbm.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + diff --git a/parts/res/xml/charge_settings.xml b/parts/res/xml/charge_settings.xml new file mode 100644 index 00000000..fa81b16b --- /dev/null +++ b/parts/res/xml/charge_settings.xml @@ -0,0 +1,8 @@ + + + + diff --git a/parts/res/xml/clear_speaker_settings.xml b/parts/res/xml/clear_speaker_settings.xml new file mode 100644 index 00000000..8716efbe --- /dev/null +++ b/parts/res/xml/clear_speaker_settings.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/parts/res/xml/core_control_settings.xml b/parts/res/xml/core_control_settings.xml new file mode 100644 index 00000000..15b10d5b --- /dev/null +++ b/parts/res/xml/core_control_settings.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/parts/res/xml/game_bar_preferences.xml b/parts/res/xml/game_bar_preferences.xml new file mode 100644 index 00000000..cb8f388c --- /dev/null +++ b/parts/res/xml/game_bar_preferences.xml @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parts/res/xml/saturation.xml b/parts/res/xml/saturation.xml new file mode 100644 index 00000000..58dfedaf --- /dev/null +++ b/parts/res/xml/saturation.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + diff --git a/parts/res/xml/sound_control_settings.xml b/parts/res/xml/sound_control_settings.xml new file mode 100644 index 00000000..bb1d4a19 --- /dev/null +++ b/parts/res/xml/sound_control_settings.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/parts/src/org/lineageos/settings/BootCompletedReceiver.java b/parts/src/org/lineageos/settings/BootCompletedReceiver.java new file mode 100644 index 00000000..cf5a7c01 --- /dev/null +++ b/parts/src/org/lineageos/settings/BootCompletedReceiver.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * 2017-2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +import android.view.Display; +import android.view.Display.HdrCapabilities; + +import org.lineageos.settings.thermal.ThermalUtils; +import org.lineageos.settings.refreshrate.RefreshUtils; +import org.lineageos.settings.soundcontrol.SoundControlUtils; + +public class BootCompletedReceiver extends BroadcastReceiver { + private static final boolean DEBUG = false; + private static final String TAG = "XiaomiParts"; + + @Override + public void onReceive(final Context context, Intent intent) { + if (DEBUG) Log.i(TAG, "Received intent: " + intent.getAction()); + switch (intent.getAction()) { + case Intent.ACTION_LOCKED_BOOT_COMPLETED: + onLockedBootCompleted(context); + break; + case Intent.ACTION_BOOT_COMPLETED: + onBootCompleted(context); + break; + } + } + + private static void onLockedBootCompleted(Context context) { + ThermalUtils.startService(context); + RefreshUtils.startService(context); + overrideHdrTypes(context); + SoundControlUtils.applyAll(context); + } + + private static void overrideHdrTypes(Context context) { + // Override HDR types to enable Dolby Vision + final DisplayManager dm = context.getSystemService(DisplayManager.class); + dm.overrideHdrTypes(Display.DEFAULT_DISPLAY, new int[]{ + HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HDR10, + HdrCapabilities.HDR_TYPE_HLG, HdrCapabilities.HDR_TYPE_HDR10_PLUS}); + } + + private static void onBootCompleted(Context context) { + } +} diff --git a/parts/src/org/lineageos/settings/Constants.java b/parts/src/org/lineageos/settings/Constants.java new file mode 100644 index 00000000..85bf14c6 --- /dev/null +++ b/parts/src/org/lineageos/settings/Constants.java @@ -0,0 +1,32 @@ +/* + * 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; + +public class Constants { + + // AutoHbm + public static final String KEY_AUTO_HBM = "auto_hbm"; + public static final String KEY_AUTO_HBM_THRESHOLD = "auto_hbm_threshold"; + public static final String KEY_AUTO_HBM_ENABLE_TIME = "auto_hbm_enable_time"; + public static final String KEY_AUTO_HBM_DISABLE_TIME = "auto_hbm_disable_time"; + public static final String KEY_CURRENT_LUX_LEVEL = "current_lux_level"; + public static final String NODE_BRIGHTNESS = "/sys/class/backlight/panel0-backlight/brightness"; + + // Saturation + public static final String KEY_SATURATION = "saturation"; + public static final String KEY_SATURATION_PREVIEW = "saturation_preview"; +} diff --git a/parts/src/org/lineageos/settings/CustomSeekBarPreference.java b/parts/src/org/lineageos/settings/CustomSeekBarPreference.java new file mode 100644 index 00000000..bd4e35dd --- /dev/null +++ b/parts/src/org/lineageos/settings/CustomSeekBarPreference.java @@ -0,0 +1,368 @@ +/* + * 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; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.PorterDuff; +import androidx.core.content.res.TypedArrayUtils; +import androidx.preference.*; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import org.lineageos.settings.R; + +public class CustomSeekBarPreference extends Preference implements SeekBar.OnSeekBarChangeListener { + protected final String TAG = getClass().getName(); + private static final String SETTINGS_NS = "http://schemas.android.com/apk/res/com.android.settings"; + protected static final String ANDROIDNS = "http://schemas.android.com/apk/res/android"; + + protected int mInterval = 1; + protected boolean mShowSign = false; + protected String mUnits = ""; + protected boolean mContinuousUpdates = false; + + protected int mMinValue = 0; + protected int mMaxValue = 100; + protected boolean mDefaultValueExists = false; + protected int mDefaultValue; + protected boolean mDefaultValueTextExists = false; + protected String mDefaultValueText; + + protected int mValue; + + protected TextView mValueTextView; + protected ImageView mResetImageView; + protected ImageView mMinusImageView; + protected ImageView mPlusImageView; + protected SeekBar mSeekBar; + + protected boolean mTrackingTouch = false; + protected int mTrackingValue; + + public CustomSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomSeekBarPreference); + try { + mShowSign = a.getBoolean(R.styleable.CustomSeekBarPreference_showSign, mShowSign); + String units = a.getString(R.styleable.CustomSeekBarPreference_units); + if (units != null) + mUnits = " " + units; + mContinuousUpdates = a.getBoolean(R.styleable.CustomSeekBarPreference_continuousUpdates, mContinuousUpdates); + String defaultValueText = a.getString(R.styleable.CustomSeekBarPreference_defaultValueText); + mDefaultValueTextExists = defaultValueText != null && !defaultValueText.isEmpty(); + if (mDefaultValueTextExists) { + mDefaultValueText = defaultValueText; + } + } finally { + a.recycle(); + } + + try { + String newInterval = attrs.getAttributeValue(SETTINGS_NS, "interval"); + if (newInterval != null) + mInterval = Integer.parseInt(newInterval); + } catch (Exception e) { + Log.e(TAG, "Invalid interval value", e); + } + mMinValue = attrs.getAttributeIntValue(SETTINGS_NS, "min", mMinValue); + mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", mMaxValue); + if (mMaxValue < mMinValue) + mMaxValue = mMinValue; + String defaultValue = attrs.getAttributeValue(ANDROIDNS, "defaultValue"); + mDefaultValueExists = defaultValue != null && !defaultValue.isEmpty(); + if (mDefaultValueExists) { + mDefaultValue = getLimitedValue(Integer.parseInt(defaultValue)); + mValue = mDefaultValue; + } else { + mValue = mMinValue; + } + + mSeekBar = new SeekBar(context, attrs); + setLayoutResource(R.layout.preference_custom_seekbar); + } + + public CustomSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public CustomSeekBarPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, + androidx.preference.R.attr.preferenceStyle, + android.R.attr.preferenceStyle)); + } + + public CustomSeekBarPreference(Context context) { + this(context, null); + } + + @Override + public void onDependencyChanged(Preference dependency, boolean disableDependent) { + super.onDependencyChanged(dependency, disableDependent); + this.setShouldDisableView(true); + if (mSeekBar != null) + mSeekBar.setEnabled(!disableDependent); + if (mResetImageView != null) + mResetImageView.setEnabled(!disableDependent); + if (mPlusImageView != null) + mPlusImageView.setEnabled(!disableDependent); + if (mMinusImageView != null) + mMinusImageView.setEnabled(!disableDependent); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + try + { + // move our seekbar to the new view we've been given + ViewParent oldContainer = mSeekBar.getParent(); + ViewGroup newContainer = (ViewGroup) holder.findViewById(R.id.seekbar); + if (oldContainer != newContainer) { + // remove the seekbar from the old view + if (oldContainer != null) { + ((ViewGroup) oldContainer).removeView(mSeekBar); + } + // remove the existing seekbar (there may not be one) and add ours + newContainer.removeAllViews(); + newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + } catch (Exception ex) { + Log.e(TAG, "Error binding view: " + ex.toString()); + } + + mSeekBar.setMax(getSeekValue(mMaxValue)); + mSeekBar.setProgress(getSeekValue(mValue)); + mSeekBar.setEnabled(isEnabled()); + + mValueTextView = (TextView) holder.findViewById(R.id.value); + mResetImageView = (ImageView) holder.findViewById(R.id.reset); + mMinusImageView = (ImageView) holder.findViewById(R.id.minus); + mPlusImageView = (ImageView) holder.findViewById(R.id.plus); + + updateValueViews(); + + mSeekBar.setOnSeekBarChangeListener(this); + mResetImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Toast.makeText(getContext(), getContext().getString(R.string.custom_seekbar_default_value_to_set, getTextValue(mDefaultValue)), + Toast.LENGTH_LONG).show(); + } + }); + mResetImageView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + setValue(mDefaultValue, true); + return true; + } + }); + mMinusImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + setValue(mValue - mInterval, true); + } + }); + mMinusImageView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue < mValue * 2 ? Math.floorDiv(mMaxValue + mMinValue, 2) : mMinValue, true); + return true; + } + }); + mPlusImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + setValue(mValue + mInterval, true); + } + }); + mPlusImageView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue > mValue * 2 ? -1 * Math.floorDiv(-1 * (mMaxValue + mMinValue), 2) : mMaxValue, true); + return true; + } + }); + } + + protected int getLimitedValue(int v) { + return v < mMinValue ? mMinValue : (v > mMaxValue ? mMaxValue : v); + } + + protected int getSeekValue(int v) { + return 0 - Math.floorDiv(mMinValue - v, mInterval); + } + + protected String getTextValue(int v) { + if (mDefaultValueTextExists && mDefaultValueExists && v == mDefaultValue) { + return mDefaultValueText; + } + return (mShowSign && v > 0 ? "+" : "") + String.valueOf(v) + mUnits; + } + + protected void updateValueViews() { + if (mValueTextView != null) { + if (!mTrackingTouch || mContinuousUpdates) { + if (mDefaultValueTextExists && mDefaultValueExists && mValue == mDefaultValue) { + mValueTextView.setText(mDefaultValueText + " (" + + getContext().getString(R.string.custom_seekbar_default_value) + ")"); + } else { + mValueTextView.setText(getContext().getString(R.string.custom_seekbar_value, getTextValue(mValue)) + + (mDefaultValueExists && mValue == mDefaultValue ? " (" + + getContext().getString(R.string.custom_seekbar_default_value) + ")" : "")); + } + } else { + if (mDefaultValueTextExists && mDefaultValueExists && mTrackingValue == mDefaultValue) { + mValueTextView.setText("[" + mDefaultValueText + "]"); + } else { + mValueTextView.setText(getContext().getString(R.string.custom_seekbar_value, "[" + getTextValue(mTrackingValue) + "]")); + } + } + } + if (mResetImageView != null) { + if (!mDefaultValueExists || mValue == mDefaultValue || mTrackingTouch) + mResetImageView.setVisibility(View.INVISIBLE); + else + mResetImageView.setVisibility(View.VISIBLE); + } + if (mMinusImageView != null) { + if (mValue == mMinValue || mTrackingTouch) { + mMinusImageView.setClickable(false); + mMinusImageView.setColorFilter(getContext().getColor(R.color.disabled_text_color), + PorterDuff.Mode.MULTIPLY); + } else { + mMinusImageView.setClickable(true); + mMinusImageView.clearColorFilter(); + } + } + if (mPlusImageView != null) { + if (mValue == mMaxValue || mTrackingTouch) { + mPlusImageView.setClickable(false); + mPlusImageView.setColorFilter(getContext().getColor(R.color.disabled_text_color), PorterDuff.Mode.MULTIPLY); + } else { + mPlusImageView.setClickable(true); + mPlusImageView.clearColorFilter(); + } + } + } + + protected void changeValue(int newValue) { + // for subclasses + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + int newValue = getLimitedValue(mMinValue + (progress * mInterval)); + if (mTrackingTouch && !mContinuousUpdates) { + mTrackingValue = newValue; + updateValueViews(); + } else if (mValue != newValue) { + // change rejected, revert to the previous value + if (!callChangeListener(newValue)) { + mSeekBar.setProgress(getSeekValue(mValue)); + return; + } + // change accepted, store it + changeValue(newValue); + persistInt(newValue); + + mValue = newValue; + updateValueViews(); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mTrackingValue = mValue; + mTrackingTouch = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + mTrackingTouch = false; + if (!mContinuousUpdates) + onProgressChanged(mSeekBar, getSeekValue(mTrackingValue), false); + notifyChanged(); + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + if (restoreValue) + mValue = getPersistedInt(mValue); + } + + @Override + public void setDefaultValue(Object defaultValue) { + if (defaultValue instanceof Integer) + setDefaultValue((Integer) defaultValue, mSeekBar != null); + else + setDefaultValue(defaultValue == null ? (String) null : defaultValue.toString(), mSeekBar != null); + } + + public void setDefaultValue(int newValue, boolean update) { + newValue = getLimitedValue(newValue); + if (!mDefaultValueExists || mDefaultValue != newValue) { + mDefaultValueExists = true; + mDefaultValue = newValue; + if (update) + updateValueViews(); + } + } + + public void setDefaultValue(String newValue, boolean update) { + if (mDefaultValueExists && (newValue == null || newValue.isEmpty())) { + mDefaultValueExists = false; + if (update) + updateValueViews(); + } else if (newValue != null && !newValue.isEmpty()) { + setDefaultValue(Integer.parseInt(newValue), update); + } + } + + public void setValue(int newValue) { + mValue = getLimitedValue(newValue); + if (mSeekBar != null) mSeekBar.setProgress(getSeekValue(mValue)); + } + + public void setValue(int newValue, boolean update) { + newValue = getLimitedValue(newValue); + if (mValue != newValue) { + if (update) + mSeekBar.setProgress(getSeekValue(newValue)); + else + mValue = newValue; + } + } + + public int getValue() { + return mValue; + } + + public void refresh(int newValue) { + // this will ... + setValue(newValue, mSeekBar != null); + } +} diff --git a/parts/src/org/lineageos/settings/Startup.java b/parts/src/org/lineageos/settings/Startup.java new file mode 100644 index 00000000..4897d86c --- /dev/null +++ b/parts/src/org/lineageos/settings/Startup.java @@ -0,0 +1,113 @@ +/* + * 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; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.Constants; +import org.lineageos.settings.autohbm.AutoHbmActivity; +import org.lineageos.settings.autohbm.AutoHbmFragment; +import org.lineageos.settings.autohbm.AutoHbmTileService; +import org.lineageos.settings.saturation.SaturationFragment; +import org.lineageos.settings.utils.ComponentUtils; +import org.lineageos.settings.utils.FileUtils; + +public class Startup extends BroadcastReceiver { + + private static final String TAG = "Startup"; + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + Log.d(TAG, "onReceive called with action: " + action); + + if (Intent.ACTION_BOOT_COMPLETED.equals(action) || + Intent.ACTION_REBOOT.equals(action)) { + + // Adding a delay before applying the settings + new Handler(Looper.getMainLooper()).postDelayed(() -> { + Log.d(TAG, "Applying saved settings..."); + applySavedSaturation(context); + applyAutoHbmSettings(context); + }, 5000); // Delay of 5 seconds + } + } + + private void applySavedSaturation(Context context) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + int seekBarValue = sharedPrefs.getInt(Constants.KEY_SATURATION, 100); + Log.d(TAG, "Retrieved seekBarValue: " + seekBarValue); + + // Apply the saved saturation value + applySaturation(seekBarValue); + } + + private void applySaturation(int seekBarValue) { + Log.d(TAG, "Applying saturation: " + seekBarValue); + + float saturation; + if (seekBarValue == 100) { + saturation = 1.001f; + } else { + saturation = seekBarValue / 100.0f; + } + + IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + if (surfaceFlinger != null) { + try { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeFloat(saturation); + surfaceFlinger.transact(1022, data, null, 0); + data.recycle(); + Log.d(TAG, "Saturation applied successfully"); + } catch (RemoteException e) { + Log.e(TAG, "Failed to apply saturation", e); + } + } else { + Log.e(TAG, "SurfaceFlinger service not found"); + } + } + + private void applyAutoHbmSettings(Context context) { + Log.d(TAG, "Applying Auto HBM settings..."); + AutoHbmFragment.toggleAutoHbmService(context); + + ComponentUtils.toggleComponent( + context, + AutoHbmActivity.class, + true + ); + + ComponentUtils.toggleComponent( + context, + AutoHbmTileService.class, + true + ); + Log.d(TAG, "Auto HBM settings applied"); + } +} diff --git a/parts/src/org/lineageos/settings/TileHandlerActivity.java b/parts/src/org/lineageos/settings/TileHandlerActivity.java new file mode 100644 index 00000000..5f2b776e --- /dev/null +++ b/parts/src/org/lineageos/settings/TileHandlerActivity.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 kenway215 + * + * 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; + +public final class TileHandlerActivity extends Activity { + private static final String TAG = "TileHandlerActivity"; + + // Map QS Tile services to their corresponding activity + private static final Map> TILE_ACTIVITY_MAP = new HashMap<>(); + + static { + TILE_ACTIVITY_MAP.put(GameBarTileService.class.getName(), GameBarSettingsActivity.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(); + } +} diff --git a/parts/src/org/lineageos/settings/autohbm/AutoHbmActivity.java b/parts/src/org/lineageos/settings/autohbm/AutoHbmActivity.java new file mode 100644 index 00000000..695faf2c --- /dev/null +++ b/parts/src/org/lineageos/settings/autohbm/AutoHbmActivity.java @@ -0,0 +1,35 @@ + +/* + * 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.autohbm; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class AutoHbmActivity extends CollapsingToolbarBaseActivity { + + private static final String TAG = "AutoHbm"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new AutoHbmFragment(), TAG).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/autohbm/AutoHbmFragment.java b/parts/src/org/lineageos/settings/autohbm/AutoHbmFragment.java new file mode 100644 index 00000000..af7352f6 --- /dev/null +++ b/parts/src/org/lineageos/settings/autohbm/AutoHbmFragment.java @@ -0,0 +1,210 @@ +/* + * 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.autohbm; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.content.SharedPreferences; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; + +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.UsageProgressBarPreference; + +import org.lineageos.settings.Constants; +import org.lineageos.settings.CustomSeekBarPreference; +import org.lineageos.settings.R; +import org.lineageos.settings.utils.TileUtils; + +public class AutoHbmFragment extends PreferenceFragmentCompat + implements OnCheckedChangeListener, SensorEventListener, Preference.OnPreferenceChangeListener { + + private static final String[] AUTO_HBM_PREFERENCES = { + Constants.KEY_AUTO_HBM_THRESHOLD, + Constants.KEY_AUTO_HBM_ENABLE_TIME, + Constants.KEY_AUTO_HBM_DISABLE_TIME, + Constants.KEY_CURRENT_LUX_LEVEL + }; + + private CustomSeekBarPreference mAutoHbmThresholdPreference; + private MainSwitchPreference mAutoHbmSwitch; + private SensorManager mSensorManager; + private Sensor mLightSensor; + private UsageProgressBarPreference mCurrentLuxLevelPreference; + private int mCurrentLux; + + private static boolean mAutoHbmServiceEnabled = false; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.auto_hbm, rootKey); + setHasOptionsMenu(true); + + Context context = getContext(); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + mAutoHbmSwitch = findPreference(Constants.KEY_AUTO_HBM); + mAutoHbmSwitch.setChecked(sharedPrefs.getBoolean(Constants.KEY_AUTO_HBM, false)); + mAutoHbmSwitch.addOnSwitchChangeListener(this); + + mAutoHbmThresholdPreference = findPreference(Constants.KEY_AUTO_HBM_THRESHOLD); + mAutoHbmThresholdPreference.setOnPreferenceChangeListener(this); + + mCurrentLuxLevelPreference = findPreference(Constants.KEY_CURRENT_LUX_LEVEL); + + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + + toggleAutoHbmPreferencesVisibility(mAutoHbmSwitch.isChecked()); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.auto_hbm_menu, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.add_tile) { + TileUtils.requestAddTileService( + getContext(), + AutoHbmTileService.class, + R.string.auto_hbm_title, + R.drawable.ic_auto_hbm_tile + ); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onResume() { + super.onResume(); + if (mAutoHbmSwitch.isChecked()) { + mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL); + } + } + + @Override + public void onPause() { + super.onPause(); + if (mAutoHbmSwitch.isChecked()) { + mSensorManager.unregisterListener(this); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + sharedPrefs.edit().putBoolean(Constants.KEY_AUTO_HBM, isChecked).apply(); + toggleAutoHbmService(getContext()); + toggleAutoHbmPreferencesVisibility(isChecked); + + if (isChecked) { + mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL); + } else { + mSensorManager.unregisterListener(this); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mAutoHbmThresholdPreference && mCurrentLuxLevelPreference != null) { + int threshold = (int) newValue; + updateCurrentLuxLevelPreference(mCurrentLux, threshold); + return true; + } + return false; + } + + @Override + public void onSensorChanged(SensorEvent event) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + if (event.sensor.getType() == Sensor.TYPE_LIGHT && mCurrentLuxLevelPreference != null) { + float luxValue = event.values[0]; + mCurrentLux = (int) luxValue; + int threshold = sharedPrefs.getInt(Constants.KEY_AUTO_HBM_THRESHOLD, 20000); + updateCurrentLuxLevelPreference(mCurrentLux, threshold); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do nothing + } + + private void updateCurrentLuxLevelPreference(int currentLux, int threshold) { + if (mCurrentLuxLevelPreference != null) { + mCurrentLuxLevelPreference.setUsageSummary(String.valueOf(currentLux)); + mCurrentLuxLevelPreference.setTotalSummary(String.valueOf(threshold)); + + if (currentLux >= threshold) { + mCurrentLuxLevelPreference.setPercent(100, 100); + } else { + mCurrentLuxLevelPreference.setPercent(currentLux, threshold); + } + } + } + + private void toggleAutoHbmPreferencesVisibility(boolean show) { + for (String prefKey : AUTO_HBM_PREFERENCES) { + Preference pref = findPreference(prefKey); + if (pref != null) { + pref.setVisible(show); + } + } + } + + public static void toggleAutoHbmService(Context context) { + boolean isAutoHbmEnabled = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(Constants.KEY_AUTO_HBM, false); + + if (isAutoHbmEnabled && !mAutoHbmServiceEnabled) { + startAutoHbmService(context); + } else if (!isAutoHbmEnabled && mAutoHbmServiceEnabled) { + stopAutoHbmService(context); + } + } + + private static void startAutoHbmService(Context context) { + context.startServiceAsUser(new Intent(context, AutoHbmService.class), + UserHandle.CURRENT); + mAutoHbmServiceEnabled = true; + } + + private static void stopAutoHbmService(Context context) { + mAutoHbmServiceEnabled = false; + context.stopServiceAsUser(new Intent(context, AutoHbmService.class), + UserHandle.CURRENT); + } +} diff --git a/parts/src/org/lineageos/settings/autohbm/AutoHbmService.java b/parts/src/org/lineageos/settings/autohbm/AutoHbmService.java new file mode 100644 index 00000000..2d481563 --- /dev/null +++ b/parts/src/org/lineageos/settings/autohbm/AutoHbmService.java @@ -0,0 +1,191 @@ +/* + * 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.autohbm; + +import android.app.KeyguardManager; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.IBinder; +import android.os.PowerManager; +import androidx.preference.PreferenceManager; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.lineageos.settings.Constants; +import org.lineageos.settings.utils.FileUtils; + +public class AutoHbmService extends Service { + + private static final int MAX_BRIGHTNESS = 4000; + private static final int FALLBACK_BRIGHTNESS = 200; + private static boolean mAutoHbmActive = false; + private ExecutorService mExecutorService; + + private SensorManager mSensorManager; + private Sensor mLightSensor; + + private SharedPreferences mSharedPrefs; + private int mLastManualBrightness = FALLBACK_BRIGHTNESS; + private boolean mIsAutoBrightnessEnabled = false; + + public void activateLightSensorRead() { + submit(() -> { + mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE); + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + mSensorManager.registerListener(mSensorEventListener, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL); + }); + } + + public void deactivateLightSensorRead() { + submit(() -> { + mSensorManager.unregisterListener(mSensorEventListener); + mAutoHbmActive = false; + restoreBrightness(); + }); + } + + private void setBrightnessDirectly(int brightness) { + FileUtils.writeValue(Constants.NODE_BRIGHTNESS, String.valueOf(brightness)); + } + + private void restoreBrightness() { + if (mIsAutoBrightnessEnabled) { + // Auto-brightness will handle the adjustment + } else { + setBrightnessDirectly(mLastManualBrightness > 0 ? mLastManualBrightness : FALLBACK_BRIGHTNESS); + } + } + + private boolean isCurrentlyEnabled() { + String fileValue = FileUtils.getFileValue(Constants.NODE_BRIGHTNESS, "0"); + return Integer.parseInt(fileValue) == MAX_BRIGHTNESS; + } + + SensorEventListener mSensorEventListener = new SensorEventListener() { + private boolean mCrossedThreshold = false; + private long mCrossedThresholdTime = 0; + private long mLastTriggerTime = 0; + + @Override + public void onSensorChanged(SensorEvent event) { + float lux = event.values[0]; + KeyguardManager km = (KeyguardManager) getSystemService(getApplicationContext().KEYGUARD_SERVICE); + boolean keyguardShowing = km.inKeyguardRestrictedInputMode(); + int luxThreshold = mSharedPrefs.getInt(Constants.KEY_AUTO_HBM_THRESHOLD, 20000); + int timeToEnableHbm = mSharedPrefs.getInt(Constants.KEY_AUTO_HBM_ENABLE_TIME, 0); + int timeToDisableHbm = mSharedPrefs.getInt(Constants.KEY_AUTO_HBM_DISABLE_TIME, 1); + + if (lux > luxThreshold) { + if (!mCrossedThreshold) { + mCrossedThreshold = true; + mCrossedThresholdTime = System.currentTimeMillis(); + } else { + long currentTime = System.currentTimeMillis(); + if (currentTime - mCrossedThresholdTime >= timeToEnableHbm * 1000 && (!mAutoHbmActive || !isCurrentlyEnabled()) && !keyguardShowing) { + mAutoHbmActive = true; + saveCurrentBrightness(); + setBrightnessDirectly(MAX_BRIGHTNESS); + mLastTriggerTime = currentTime; + } + } + } else { + mCrossedThreshold = false; + + if (mAutoHbmActive) { + long currentTime = System.currentTimeMillis(); + if (currentTime - mLastTriggerTime >= timeToDisableHbm * 1000) { + mAutoHbmActive = false; + restoreBrightness(); + } + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // do nothing + } + }; + + private void saveCurrentBrightness() { + try { + String fileValue = FileUtils.getFileValue(Constants.NODE_BRIGHTNESS, String.valueOf(FALLBACK_BRIGHTNESS)); + mLastManualBrightness = Integer.parseInt(fileValue); + } catch (NumberFormatException e) { + mLastManualBrightness = FALLBACK_BRIGHTNESS; + } + mIsAutoBrightnessEnabled = mSharedPrefs.getBoolean("auto_brightness", false); + } + + private BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + activateLightSensorRead(); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + deactivateLightSensorRead(); + } + } + }; + + @Override + public void onCreate() { + mExecutorService = Executors.newSingleThreadExecutor(); + IntentFilter screenStateFilter = new IntentFilter(Intent.ACTION_SCREEN_ON); + screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF); + registerReceiver(mScreenStateReceiver, screenStateFilter); + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (pm.isInteractive()) { + activateLightSensorRead(); + } + } + + private Future submit(Runnable runnable) { + return mExecutorService.submit(runnable); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceiver(mScreenStateReceiver); + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (pm.isInteractive()) { + deactivateLightSensorRead(); + } + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/parts/src/org/lineageos/settings/autohbm/AutoHbmTileService.java b/parts/src/org/lineageos/settings/autohbm/AutoHbmTileService.java new file mode 100644 index 00000000..e5db62c8 --- /dev/null +++ b/parts/src/org/lineageos/settings/autohbm/AutoHbmTileService.java @@ -0,0 +1,65 @@ +/* + * 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.autohbm; + +import android.content.SharedPreferences; +import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.Constants; +import org.lineageos.settings.R; +import org.lineageos.settings.utils.FileUtils; + +public class AutoHbmTileService extends TileService { + @Override + public void onStartListening() { + super.onStartListening(); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + updateTile(sharedPrefs.getBoolean(Constants.KEY_AUTO_HBM, false)); + } + + @Override + public void onStopListening() { + super.onStopListening(); + } + + @Override + public void onClick() { + super.onClick(); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final boolean enabled = !(sharedPrefs.getBoolean(Constants.KEY_AUTO_HBM, false)); + sharedPrefs.edit().putBoolean(Constants.KEY_AUTO_HBM, enabled).commit(); + AutoHbmFragment.toggleAutoHbmService(this); + updateTile(enabled); + } + + private void updateTile(boolean enabled) { + final Tile tile = getQsTile(); + tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); + + // Set the title for the tile using the new string resource + String title = getString(R.string.tile_auto_hbm); + tile.setLabel(title); + + // Set the subtitle based on the state + String subtitle = enabled ? getString(R.string.tile_hbm_on) : getString(R.string.tile_hbm_off); + tile.setSubtitle(subtitle); + + tile.updateTile(); + } +} diff --git a/parts/src/org/lineageos/settings/autohbm/HbmTileService.java b/parts/src/org/lineageos/settings/autohbm/HbmTileService.java new file mode 100644 index 00000000..60c78be5 --- /dev/null +++ b/parts/src/org/lineageos/settings/autohbm/HbmTileService.java @@ -0,0 +1,158 @@ +/* + * 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.autohbm; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; +import androidx.preference.PreferenceManager; +import org.lineageos.settings.Constants; +import org.lineageos.settings.R; +import org.lineageos.settings.utils.FileUtils; + +public class HbmTileService extends TileService { + private static final int MAX_BRIGHTNESS = 4000; + private static final int FALLBACK_BRIGHTNESS = 200; + private static final String NOTIFICATION_CHANNEL_ID = "hbm_tile_service_channel"; + private static final int NOTIFICATION_ID = 2; + private int mLastManualBrightness = FALLBACK_BRIGHTNESS; + private boolean mIsAutoBrightnessEnabled = false; + private NotificationManager mNotificationManager; + + @Override + public void onCreate() { + super.onCreate(); + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + setupNotificationChannel(); + } + + @Override + public void onStartListening() { + super.onStartListening(); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean isAutoHbmEnabled = sharedPrefs.getBoolean(Constants.KEY_AUTO_HBM, false); + mIsAutoBrightnessEnabled = isSystemAutoBrightnessEnabled(); + boolean isHbmEnabled = isCurrentlyEnabled(); + updateTile(isAutoHbmEnabled, mIsAutoBrightnessEnabled, isHbmEnabled); + } + + @Override + public void onClick() { + super.onClick(); + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean isAutoHbmEnabled = sharedPrefs.getBoolean(Constants.KEY_AUTO_HBM, false); + mIsAutoBrightnessEnabled = isSystemAutoBrightnessEnabled(); + + if (isAutoHbmEnabled || mIsAutoBrightnessEnabled) { + // Do nothing if Auto HBM or Auto Brightness is enabled + return; + } + + boolean isHbmEnabled = isCurrentlyEnabled(); + if (isHbmEnabled) { + restoreBrightness(); + cancelHbmNotification(); + } else { + saveCurrentBrightness(); + setBrightnessDirectly(MAX_BRIGHTNESS); + showHbmNotification(); + } + updateTile(isAutoHbmEnabled, mIsAutoBrightnessEnabled, !isHbmEnabled); + } + + private void updateTile(boolean isAutoHbmEnabled, boolean isAutoBrightnessEnabled, boolean isHbmEnabled) { + final Tile tile = getQsTile(); + if (isAutoHbmEnabled || isAutoBrightnessEnabled) { + tile.setState(Tile.STATE_UNAVAILABLE); + tile.setLabel(getString(R.string.tile_auto_hbm_or_brightness_enabled)); + tile.setSubtitle(null); + } else { + tile.setState(isHbmEnabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); + tile.setLabel(getString(R.string.tile_hbm)); + tile.setSubtitle(isHbmEnabled ? getString(R.string.tile_hbm_on) : getString(R.string.tile_hbm_off)); + } + tile.updateTile(); + } + + private void setBrightnessDirectly(int brightness) { + FileUtils.writeValue(Constants.NODE_BRIGHTNESS, String.valueOf(brightness)); + } + + private void restoreBrightness() { + if (mIsAutoBrightnessEnabled) { + return; + } + setBrightnessDirectly(mLastManualBrightness > 0 ? mLastManualBrightness : FALLBACK_BRIGHTNESS); + } + + private boolean isCurrentlyEnabled() { + String fileValue = FileUtils.getFileValue(Constants.NODE_BRIGHTNESS, "0"); + return Integer.parseInt(fileValue) == MAX_BRIGHTNESS; + } + + private void saveCurrentBrightness() { + try { + String fileValue = FileUtils.getFileValue(Constants.NODE_BRIGHTNESS, String.valueOf(FALLBACK_BRIGHTNESS)); + mLastManualBrightness = Integer.parseInt(fileValue); + } catch (NumberFormatException e) { + mLastManualBrightness = FALLBACK_BRIGHTNESS; + } + } + + private boolean isSystemAutoBrightnessEnabled() { + try { + return Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE) == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; + } catch (Settings.SettingNotFoundException e) { + return false; + } + } + + private void setupNotificationChannel() { + NotificationChannel channel = new NotificationChannel( + NOTIFICATION_CHANNEL_ID, + getString(R.string.hbm_mode_title), + NotificationManager.IMPORTANCE_DEFAULT + ); + channel.setBlockable(true); + mNotificationManager.createNotificationChannel(channel); + } + + private void showHbmNotification() { + Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); + Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) + .setContentTitle(getString(R.string.hbm_mode_title)) + .setContentText(getString(R.string.hbm_mode_notification)) + .setSmallIcon(R.drawable.ic_auto_hbm_tile) + .setContentIntent(pendingIntent) + .setOngoing(true) + .setFlag(Notification.FLAG_NO_CLEAR, true) + .build(); + mNotificationManager.notify(NOTIFICATION_ID, notification); + } + + private void cancelHbmNotification() { + mNotificationManager.cancel(NOTIFICATION_ID); + } +} diff --git a/parts/src/org/lineageos/settings/charge/ChargeActivity.java b/parts/src/org/lineageos/settings/charge/ChargeActivity.java new file mode 100644 index 00000000..003bd7c4 --- /dev/null +++ b/parts/src/org/lineageos/settings/charge/ChargeActivity.java @@ -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); + + getFragmentManager().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; + } +} diff --git a/parts/src/org/lineageos/settings/charge/ChargeSettingsFragment.java b/parts/src/org/lineageos/settings/charge/ChargeSettingsFragment.java new file mode 100644 index 00000000..8b6c6ddc --- /dev/null +++ b/parts/src/org/lineageos/settings/charge/ChargeSettingsFragment.java @@ -0,0 +1,80 @@ +/* + * 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 androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragment; +import androidx.preference.SwitchPreference; + +import org.lineageos.settings.R; + +public class ChargeSettingsFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private static final String KEY_BYPASS_CHARGE = "bypass_charge"; + private SwitchPreference mBypassChargePreference; + private ChargeUtils mChargeUtils; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.charge_settings, rootKey); + + mChargeUtils = new ChargeUtils(getActivity()); + mBypassChargePreference = (SwitchPreference) findPreference(KEY_BYPASS_CHARGE); + + boolean bypassChargeSupported = mChargeUtils.isBypassChargeSupported(); + + if (mBypassChargePreference != null) { + mBypassChargePreference.setEnabled(bypassChargeSupported); + if (bypassChargeSupported) { + mBypassChargePreference.setChecked(mChargeUtils.isBypassChargeEnabled()); + mBypassChargePreference.setOnPreferenceChangeListener(this); + } else { + mBypassChargePreference.setSummary(R.string.charge_bypass_unavailable); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final String key = preference.getKey(); + + if (KEY_BYPASS_CHARGE.equals(key)) { + boolean bypassValue = (Boolean) newValue; + if (bypassValue) { + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.charge_bypass_title) + .setMessage(R.string.charge_bypass_warning) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + mChargeUtils.enableBypassCharge(true); + mBypassChargePreference.setChecked(true); + }) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + mBypassChargePreference.setChecked(false); + }) + .show(); + return false; + } else { + mChargeUtils.enableBypassCharge(false); + return true; + } + } + return false; + } +} diff --git a/parts/src/org/lineageos/settings/charge/ChargeUtils.java b/parts/src/org/lineageos/settings/charge/ChargeUtils.java new file mode 100644 index 00000000..2d410231 --- /dev/null +++ b/parts/src/org/lineageos/settings/charge/ChargeUtils.java @@ -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.Context; +import android.content.SharedPreferences; +import android.util.Log; +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.utils.FileUtils; + +public class ChargeUtils { + + private static final String TAG = "ChargeUtils"; + public static final String BYPASS_CHARGE_NODE = "/sys/class/qcom-battery/input_suspend"; + private static final String PREF_BYPASS_CHARGE = "bypass_charge"; + + // Bypass modes + public static final int BYPASS_DISABLED = 0; + public static final int BYPASS_ENABLED = 1; + + private SharedPreferences mSharedPrefs; + + public ChargeUtils(Context context) { + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + 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"); + mSharedPrefs.edit().putBoolean(PREF_BYPASS_CHARGE, enable).apply(); + } catch (Exception e) { + Log.e(TAG, "Failed to write bypass charge status", e); + } + } + + public boolean isNodeAccessible(String node) { + try { + String value = 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); + } +} diff --git a/parts/src/org/lineageos/settings/corecontrol/CoreControlActivity.java b/parts/src/org/lineageos/settings/corecontrol/CoreControlActivity.java new file mode 100644 index 00000000..a5842765 --- /dev/null +++ b/parts/src/org/lineageos/settings/corecontrol/CoreControlActivity.java @@ -0,0 +1,32 @@ +/* + * 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.corecontrol; + +import android.os.Bundle; +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class CoreControlActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_CORECONTROL = "corecontrol"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getFragmentManager().beginTransaction().replace( + com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new CoreControlFragment(), TAG_CORECONTROL).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/corecontrol/CoreControlFragment.java b/parts/src/org/lineageos/settings/corecontrol/CoreControlFragment.java new file mode 100644 index 00000000..2adfc894 --- /dev/null +++ b/parts/src/org/lineageos/settings/corecontrol/CoreControlFragment.java @@ -0,0 +1,103 @@ +/* + * 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.corecontrol; + +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragment; +import androidx.preference.SwitchPreference; + +import org.lineageos.settings.R; + +import java.io.File; + +public class CoreControlFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener { + private static final String TAG = "CoreControlFragment"; + private static final int NUM_CORES = 8; + + private SwitchPreference[] mCorePrefs = new SwitchPreference[NUM_CORES]; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.core_control_settings); + + for (int i = 0; i < NUM_CORES; i++) { + String key = "core_" + i; + mCorePrefs[i] = (SwitchPreference) findPreference(key); + if (mCorePrefs[i] != null) { + mCorePrefs[i].setOnPreferenceChangeListener(this); + mCorePrefs[i].setChecked(isCoreOnline(i)); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean requestedState = (Boolean) newValue; + + for (int i = 0; i < NUM_CORES; i++) { + if (preference == mCorePrefs[i]) { + if (!requestedState && !canOffline(i)) { + Toast.makeText(getContext(), "At least 2 little cores must remain online", Toast.LENGTH_SHORT).show(); + return false; + } + setCoreState(i, requestedState); + return true; + } + } + return false; + } + + private boolean isCoreOnline(int core) { + return new File("/sys/devices/system/cpu/cpu" + core + "/online").exists() && + readFile("/sys/devices/system/cpu/cpu" + core + "/online").equals("1"); + } + + private void setCoreState(int core, boolean online) { + writeFile("/sys/devices/system/cpu/cpu" + core + "/online", online ? "1" : "0"); + } + + private boolean canOffline(int core) { + if (core >= 0 && core <= 3) { + int onlineCount = 0; + for (int i = 0; i <= 3; i++) { + if (i != core && isCoreOnline(i)) onlineCount++; + } + return onlineCount >= 2; + } + return true; + } + + private String readFile(String path) { + try { + return new String(java.nio.file.Files.readAllBytes(new File(path).toPath())).trim(); + } catch (Exception e) { + Log.e(TAG, "Failed to read " + path, e); + return ""; + } + } + + private void writeFile(String path, String value) { + try { + java.nio.file.Files.write(new File(path).toPath(), value.getBytes()); + } catch (Exception e) { + Log.e(TAG, "Failed to write " + path, e); + } + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/ForegroundAppDetector.java b/parts/src/org/lineageos/settings/gamebar/ForegroundAppDetector.java new file mode 100644 index 00000000..814aef3d --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/ForegroundAppDetector.java @@ -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 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; + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBar.java b/parts/src/org/lineageos/settings/gamebar/GameBar.java new file mode 100644 index 00000000..afa95afe --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBar.java @@ -0,0 +1,795 @@ +/* + * 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/power_supply/battery/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 boolean mShowRamSpeed = false; + private boolean mShowRamTemp = false; + + 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); + + mShowRamSpeed = prefs.getBoolean("game_bar_ram_speed_enable", false); + mShowRamTemp = prefs.getBoolean("game_bar_ram_temp_enable", 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 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 / 10f; + 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 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")); + } + + // 6.1) RAM speed + if (mShowRamSpeed) { + String ramSpeedStr = GameBarMemInfo.getRamSpeed(); + statViews.add(createStatLine("RAM Freq", ramSpeedStr)); + } + + // 6.2) RAM temp + if (mShowRamTemp) { + String ramTempStr = GameBarMemInfo.getRamTemp(); + statViews.add(createStatLine("RAM Temp", ramTempStr)); + } + + // 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 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 setShowRamSpeed(boolean show) { mShowRamSpeed = show; } + public void setShowRamTemp(boolean show) { mShowRamTemp = 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); + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarBootReceiver.java b/parts/src/org/lineageos/settings/gamebar/GameBarBootReceiver.java new file mode 100644 index 00000000..fc8b5615 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarBootReceiver.java @@ -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); + } + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarCpuInfo.java b/parts/src/org/lineageos/settings/gamebar/GameBarCpuInfo.java new file mode 100644 index 00000000..fc63fead --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarCpuInfo.java @@ -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_zone0/temp"; + + public static String getCpuUsage() { + String line = readLine("/proc/stat"); + if (line == null || !line.startsWith("cpu ")) return "N/A"; + String[] parts = line.split("\\s+"); + if (parts.length < 8) return "N/A"; + + try { + long user = Long.parseLong(parts[1]); + long nice = Long.parseLong(parts[2]); + long system = Long.parseLong(parts[3]); + long idle = Long.parseLong(parts[4]); + long iowait = Long.parseLong(parts[5]); + long irq = Long.parseLong(parts[6]); + long softirq = Long.parseLong(parts[7]); + long steal = parts.length > 8 ? Long.parseLong(parts[8]) : 0; + + long total = user + nice + system + idle + iowait + irq + softirq + steal; + + if (sPrevTotal != -1 && total != sPrevTotal) { + long diffTotal = total - sPrevTotal; + long diffIdle = idle - sPrevIdle; + long usage = 100 * (diffTotal - diffIdle) / diffTotal; + sPrevTotal = total; + sPrevIdle = idle; + return String.valueOf(usage); + } else { + + sPrevTotal = total; + sPrevIdle = idle; + return "N/A"; + } + } catch (NumberFormatException e) { + return "N/A"; + } + } + + public static List getCpuFrequencies() { + List 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 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; + } + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarFpsMeter.java b/parts/src/org/lineageos/settings/gamebar/GameBarFpsMeter.java new file mode 100644 index 00000000..ff99bb9c --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarFpsMeter.java @@ -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); + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarFragment.java b/parts/src/org/lineageos/settings/gamebar/GameBarFragment.java new file mode 100644 index 00000000..7d7f01bc --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarFragment.java @@ -0,0 +1,401 @@ +/* + * 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; + private SwitchPreferenceCompat mRamSpeedSwitch; + private SwitchPreferenceCompat mRamTempSwitch; + + @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"); + mRamSpeedSwitch = findPreference("game_bar_ram_speed_enable"); + mRamTempSwitch = findPreference("game_bar_ram_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 perAppConfigPref = findPreference("game_bar_per_app_config"); + if (perAppConfigPref != null) { + perAppConfigPref.setOnPreferenceClickListener(pref -> { + startActivity(new android.content.Intent(getContext(), GameBarPerAppConfigActivity.class)); + 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 (mRamSpeedSwitch != null) { + mRamSpeedSwitch.setOnPreferenceChangeListener((pref, newValue) -> { + mGameBar.setShowRamSpeed((boolean) newValue); + return true; + }); + } + if (mRamTempSwitch != null) { + mRamTempSwitch.setOnPreferenceChangeListener((pref, newValue) -> { + mGameBar.setShowRamTemp((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); + } +} + diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarGpuInfo.java b/parts/src/org/lineageos/settings/gamebar/GameBarGpuInfo.java new file mode 100644 index 00000000..7f7fe8e7 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarGpuInfo.java @@ -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; + } + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarMemInfo.java b/parts/src/org/lineageos/settings/gamebar/GameBarMemInfo.java new file mode 100644 index 00000000..1ef94506 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarMemInfo.java @@ -0,0 +1,103 @@ +/* + * 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; + } + } + + public static String getRamSpeed() { + String path = "/sys/devices/system/cpu/bus_dcvs/DDR/cur_freq"; + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + String line = br.readLine(); + if (line != null && !line.isEmpty()) { + try { + int khz = Integer.parseInt(line.trim()); + float mhz = khz / 1000f; + if (mhz >= 1000) { + return String.format("%.3f GHz", mhz / 1000f); + } else { + return String.format("%.0f MHz", mhz); + } + } catch (NumberFormatException ignored) {} + } + } catch (IOException e) { + // ignore + } + return "N/A"; + } + + public static String getRamTemp() { + String path = "/sys/class/thermal/thermal_zone27/temp"; + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + String line = br.readLine(); + if (line != null && !line.isEmpty()) { + try { + int raw = Integer.parseInt(line.trim()); + float c = raw / 1000f; + return String.format("%.1f°C", c); + } catch (NumberFormatException ignored) {} + } + } catch (IOException e) { + // ignore + } + return "N/A"; + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarMonitorService.java b/parts/src/org/lineageos/settings/gamebar/GameBarMonitorService.java new file mode 100644 index 00000000..6543831f --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarMonitorService.java @@ -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 autoApps = prefs.getStringSet(org.lineageos.settings.gamebar.GameBarPerAppConfigFragment.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); + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigActivity.java b/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigActivity.java new file mode 100644 index 00000000..b8a191ae --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigActivity.java @@ -0,0 +1,35 @@ +/* + * 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 GameBarPerAppConfigActivity extends CollapsingToolbarBaseActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_game_bar_app_selector); + setTitle("Configure Per-App GameBar"); + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .replace(R.id.content_frame, new GameBarPerAppConfigFragment()) + .commit(); + } + } +} \ No newline at end of file diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigFragment.java b/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigFragment.java new file mode 100644 index 00000000..50c5a465 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarPerAppConfigFragment.java @@ -0,0 +1,124 @@ +/* + * 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.os.Bundle; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; +import androidx.preference.SwitchPreferenceCompat; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import android.widget.EditText; +import android.text.TextWatcher; +import android.text.Editable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.os.Build; +import android.widget.LinearLayout; +import android.content.Context; +import org.lineageos.settings.R; + +public class GameBarPerAppConfigFragment extends PreferenceFragmentCompat { + public static final String PREF_AUTO_APPS = "game_bar_auto_apps"; + + private EditText mSearchBar; + private PreferenceCategory mCategory; + private List mAllApps; + private PackageManager mPm; + private Set mAutoApps; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, container, savedInstanceState); + Context context = getContext(); + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + mSearchBar = new EditText(context); + mSearchBar.setId(View.generateViewId()); + mSearchBar.setHint("Search apps..."); + mSearchBar.setInputType(android.text.InputType.TYPE_CLASS_TEXT); + mSearchBar.setBackgroundResource(R.drawable.bg_search_rounded); + mSearchBar.setPadding(24, 24, 24, 24); + mSearchBar.setTextColor(context.getColor(R.color.app_name_text_selector)); + mSearchBar.setHintTextColor(context.getColor(R.color.app_package_text_selector)); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + int margin = (int) (context.getResources().getDisplayMetrics().density * 16); // 16dp + params.setMargins(margin, 0, margin, 0); + layout.addView(mSearchBar, params); + if (root != null) { + layout.addView(root); + } + mSearchBar.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) { + populateAppList(s.toString().toLowerCase()); + } + @Override public void afterTextChanged(Editable s) {} + }); + return layout; + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext())); + mCategory = new PreferenceCategory(getContext()); + mCategory.setTitle("Configure Per-App GameBar"); + getPreferenceScreen().addPreference(mCategory); + mPm = requireContext().getPackageManager(); + mAllApps = mPm.getInstalledApplications(PackageManager.GET_META_DATA); + mAutoApps = PreferenceManager.getDefaultSharedPreferences(getContext()) + .getStringSet(PREF_AUTO_APPS, new HashSet<>()); + populateAppList(""); + } + + private void populateAppList(String filter) { + mCategory.removeAll(); + for (ApplicationInfo app : mAllApps) { + if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) continue; + if (app.packageName.equals(getContext().getPackageName())) continue; + String label = app.loadLabel(mPm).toString().toLowerCase(); + String pkg = app.packageName.toLowerCase(); + if (!filter.isEmpty() && !(label.contains(filter) || pkg.contains(filter))) continue; + SwitchPreferenceCompat pref = new SwitchPreferenceCompat(getContext()); + pref.setTitle(app.loadLabel(mPm)); + pref.setSummary(app.packageName); + pref.setKey("gamebar_" + app.packageName); + pref.setChecked(mAutoApps.contains(app.packageName)); + pref.setIcon(app.loadIcon(mPm)); + pref.setOnPreferenceChangeListener((Preference p, Object newValue) -> { + Set updated = new HashSet<>(PreferenceManager.getDefaultSharedPreferences(getContext()) + .getStringSet(PREF_AUTO_APPS, new HashSet<>())); + if ((Boolean) newValue) { + updated.add(app.packageName); + } else { + updated.remove(app.packageName); + } + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit().putStringSet(PREF_AUTO_APPS, updated).apply(); + return true; + }); + mCategory.addPreference(pref); + } + } +} \ No newline at end of file diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarSettingsActivity.java b/parts/src/org/lineageos/settings/gamebar/GameBarSettingsActivity.java new file mode 100644 index 00000000..3a6b77c5 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarSettingsActivity.java @@ -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(); + } + } + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameBarTileService.java b/parts/src/org/lineageos/settings/gamebar/GameBarTileService.java new file mode 100644 index 00000000..84c5963b --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameBarTileService.java @@ -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(); + } +} diff --git a/parts/src/org/lineageos/settings/gamebar/GameDataExport.java b/parts/src/org/lineageos/settings/gamebar/GameDataExport.java new file mode 100644 index 00000000..aa47bb15 --- /dev/null +++ b/parts/src/org/lineageos/settings/gamebar/GameDataExport.java @@ -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 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(); + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java new file mode 100644 index 00000000..02a10c22 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020-2022 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.refreshrate; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class RefreshActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_REFRESH = "refresh"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new RefreshSettingsFragment(), TAG_REFRESH).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshService.java b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java new file mode 100644 index 00000000..dbfceb7e --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.refreshrate; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ActivityTaskManager.RootTaskInfo; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; +import android.os.RemoteException; + +public class RefreshService extends Service { + + private static final String TAG = "RefreshService"; + private static final boolean DEBUG = true; + + private String mPreviousApp; + private RefreshUtils mRefreshUtils; + private IActivityTaskManager mActivityTaskManager; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mPreviousApp = ""; + } + }; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "Creating service"); + try { + mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager.registerTaskStackListener(mTaskListener); + } catch (RemoteException e) { + // Do nothing + } + mRefreshUtils = new RefreshUtils(this); + registerReceiver(); + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) Log.d(TAG, "Starting service"); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void registerReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); +// filter.addAction(Intent.ACTION_SCREEN_ON); + this.registerReceiver(mIntentReceiver, filter); + } + + private final TaskStackListener mTaskListener = new TaskStackListener() { + @Override + public void onTaskStackChanged() { + try { + final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo(); + if (info == null || info.topActivity == null) { + return; + } + String foregroundApp = info.topActivity.getPackageName(); + if (!mRefreshUtils.isAppInList) { + mRefreshUtils.getOldRate(); + } + if (!foregroundApp.equals(mPreviousApp)) { + mRefreshUtils.setRefreshRate(foregroundApp); + mPreviousApp = foregroundApp; + } + } catch (Exception e) {} + } + }; + } \ No newline at end of file diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java new file mode 100644 index 00000000..de39c026 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java @@ -0,0 +1,422 @@ +/** + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.lineageos.settings.refreshrate; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceFragment; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.settingslib.applications.ApplicationsState; + +import org.lineageos.settings.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RefreshSettingsFragment extends PreferenceFragment + implements ApplicationsState.Callbacks { + + private AllPackagesAdapter mAllPackagesAdapter; + private ApplicationsState mApplicationsState; + private ApplicationsState.Session mSession; + private ActivityFilter mActivityFilter; + private Map mEntryMap = + new HashMap(); + + private RefreshUtils mRefreshUtils; + private RecyclerView mAppsRecyclerView; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mApplicationsState.newSession(this); + mSession.onResume(); + mActivityFilter = new ActivityFilter(getActivity().getPackageManager()); + + mAllPackagesAdapter = new AllPackagesAdapter(getActivity()); + + mRefreshUtils = new RefreshUtils(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.refresh_layout, container, false); + } + + @Override + public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAppsRecyclerView = view.findViewById(R.id.refresh_rv_view); + mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + mAppsRecyclerView.setAdapter(mAllPackagesAdapter); + } + + + @Override + public void onResume() { + super.onResume(); + getActivity().setTitle(getResources().getString(R.string.refresh_title)); + rebuild(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mSession.onPause(); + mSession.onDestroy(); + } + + @Override + public void onPackageListChanged() { + mActivityFilter.updateLauncherInfoList(); + rebuild(); + } + + @Override + public void onRebuildComplete(ArrayList entries) { + if (entries != null) { + handleAppEntries(entries); + mAllPackagesAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onLoadEntriesCompleted() { + rebuild(); + } + + @Override + public void onAllSizesComputed() { + } + + @Override + public void onLauncherInfoChanged() { + } + + @Override + public void onPackageIconChanged() { + } + + @Override + public void onPackageSizeChanged(String packageName) { + } + + @Override + public void onRunningStateChanged(boolean running) { + } + + private void handleAppEntries(List entries) { + final ArrayList sections = new ArrayList(); + final ArrayList positions = new ArrayList(); + final PackageManager pm = getActivity().getPackageManager(); + String lastSectionIndex = null; + int offset = 0; + + for (int i = 0; i < entries.size(); i++) { + final ApplicationInfo info = entries.get(i).info; + final String label = (String) info.loadLabel(pm); + final String sectionIndex; + + if (!info.enabled) { + sectionIndex = "--"; // XXX + } else if (TextUtils.isEmpty(label)) { + sectionIndex = ""; + } else { + sectionIndex = label.substring(0, 1).toUpperCase(); + } + + if (lastSectionIndex == null || + !TextUtils.equals(sectionIndex, lastSectionIndex)) { + sections.add(sectionIndex); + positions.add(offset); + lastSectionIndex = sectionIndex; + } + + offset++; + } + + mAllPackagesAdapter.setEntries(entries, sections, positions); + mEntryMap.clear(); + for (ApplicationsState.AppEntry e : entries) { + mEntryMap.put(e.info.packageName, e); + } + } + + private void rebuild() { + mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR); + } + + private int getStateDrawable(int state) { + switch (state) { + case RefreshUtils.STATE_STANDARD: + return R.drawable.ic_refresh_60; + case RefreshUtils.STATE_HIGH: + return R.drawable.ic_refresh_90; + case RefreshUtils.STATE_EXTREME: + return R.drawable.ic_refresh_120; + case RefreshUtils.STATE_DEFAULT: + default: + return R.drawable.ic_refresh_default; + } + } + + private class ViewHolder extends RecyclerView.ViewHolder { + private TextView title; + private Spinner mode; + private ImageView icon; + private View rootView; + private ImageView stateIcon; + + private ViewHolder(View view) { + super(view); + this.title = view.findViewById(R.id.app_name); + this.mode = view.findViewById(R.id.app_mode); + this.icon = view.findViewById(R.id.app_icon); + this.stateIcon = view.findViewById(R.id.state); + this.rootView = view; + + view.setTag(this); + } + } + + private class ModeAdapter extends BaseAdapter { + + private final LayoutInflater inflater; + private final int[] items = { + R.string.refresh_default, + R.string.refresh_standard, + R.string.refresh_high, + R.string.refresh_extreme + }; + + private ModeAdapter(Context context) { + inflater = LayoutInflater.from(context); + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Object getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView view; + if (convertView != null) { + view = (TextView) convertView; + } else { + view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item, + parent, false); + } + + view.setText(items[position]); + view.setTextSize(14f); + + return view; + } + } + + private class AllPackagesAdapter extends RecyclerView.Adapter + implements AdapterView.OnItemSelectedListener, SectionIndexer { + + private List mEntries = new ArrayList<>(); + private String[] mSections; + private int[] mPositions; + + public AllPackagesAdapter(Context context) { + mActivityFilter = new ActivityFilter(context.getPackageManager()); + } + + @Override + public int getItemCount() { + return mEntries.size(); + } + + @Override + public long getItemId(int position) { + return mEntries.get(position).id; + } +@NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.refresh_list_item, parent, false)); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Context context = holder.itemView.getContext(); + + ApplicationsState.AppEntry entry = mEntries.get(position); + + if (entry == null) { + return; + } + holder.mode.setAdapter(new ModeAdapter(context)); + holder.mode.setOnItemSelectedListener(this); + holder.title.setText(entry.label); + holder.title.setOnClickListener(v -> holder.mode.performClick()); + mApplicationsState.ensureIcon(entry); + holder.icon.setImageDrawable(entry.icon); + int packageState = mRefreshUtils.getStateForPackage(entry.info.packageName); + holder.mode.setSelection(packageState, false); + holder.mode.setTag(entry); + holder.stateIcon.setImageResource(getStateDrawable(packageState)); + } + + private void setEntries(List entries, + List sections, List positions) { + mEntries = entries; + mSections = sections.toArray(new String[sections.size()]); + mPositions = new int[positions.size()]; + for (int i = 0; i < positions.size(); i++) { + mPositions[i] = positions.get(i); + } + notifyDataSetChanged(); + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag(); + + int currentState = mRefreshUtils.getStateForPackage(entry.info.packageName); + if (currentState != position) { + mRefreshUtils.writePackage(entry.info.packageName, position); + notifyDataSetChanged(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + @Override + public int getPositionForSection(int section) { + if (section < 0 || section >= mSections.length) { + return -1; + } + + return mPositions[section]; + } + + @Override + public int getSectionForPosition(int position) { + if (position < 0 || position >= getItemCount()) { + return -1; + } + + final int index = Arrays.binarySearch(mPositions, position); + + /* + * Consider this example: section positions are 0, 3, 5; the supplied + * position is 4. The section corresponding to position 4 starts at + * position 3, so the expected return value is 1. Binary search will not + * find 4 in the array and thus will return -insertPosition-1, i.e. -3. + * To get from that number to the expected value of 1 we need to negate + * and subtract 2. + */ + return index >= 0 ? index : -index - 2; + } + + @Override + public Object[] getSections() { + return mSections; + } + } + + private class ActivityFilter implements ApplicationsState.AppFilter { + + private final PackageManager mPackageManager; + private final List mLauncherResolveInfoList = new ArrayList(); + + private ActivityFilter(PackageManager packageManager) { + this.mPackageManager = packageManager; + + updateLauncherInfoList(); + } + + public void updateLauncherInfoList() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfoList = mPackageManager.queryIntentActivities(i, 0); + + synchronized (mLauncherResolveInfoList) { + mLauncherResolveInfoList.clear(); + for (ResolveInfo ri : resolveInfoList) { + mLauncherResolveInfoList.add(ri.activityInfo.packageName); + } + } + } + + @Override + public void init() { + } + + @Override + public boolean filterApp(ApplicationsState.AppEntry entry) { + boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName); + if (show) { + synchronized (mLauncherResolveInfoList) { + show = mLauncherResolveInfoList.contains(entry.info.packageName); + } + } + return show; + } + } +} diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java new file mode 100644 index 00000000..d9325ab9 --- /dev/null +++ b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.refreshrate; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.UserHandle; +import android.view.Display; + +import android.provider.Settings; +import androidx.preference.PreferenceManager; + +public final class RefreshUtils { + + private static final String REFRESH_CONTROL = "refresh_control"; + + private static float defaultMaxRate; + private static float defaultMinRate; + private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate"; + private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate"; + private Context mContext; + protected static boolean isAppInList = false; + + protected static final int STATE_DEFAULT = 0; + protected static final int STATE_STANDARD = 1; + protected static final int STATE_HIGH = 2; + protected static final int STATE_EXTREME = 3; + + private static final float REFRESH_STATE_DEFAULT = 120f; + private static final float REFRESH_STATE_STANDARD = 60f; + private static final float REFRESH_STATE_HIGH = 90f; + private static final float REFRESH_STATE_EXTREME = 120f; + + private static final String REFRESH_STANDARD = "refresh.standard="; + private static final String REFRESH_HIGH = "refresh.high="; + private static final String REFRESH_EXTREME = "refresh.extreme="; + + private SharedPreferences mSharedPrefs; + + protected RefreshUtils(Context context) { + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + mContext = context; + } + + public static void startService(Context context) { + context.startServiceAsUser(new Intent(context, RefreshService.class), + UserHandle.CURRENT); + } + + private void writeValue(String profiles) { + mSharedPrefs.edit().putString(REFRESH_CONTROL, profiles).apply(); + } + + protected void getOldRate(){ + defaultMaxRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, REFRESH_STATE_DEFAULT); + defaultMinRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, REFRESH_STATE_DEFAULT); + } + + + private String getValue() { + String value = mSharedPrefs.getString(REFRESH_CONTROL, null); + + if (value == null || value.isEmpty()) { + value = REFRESH_STANDARD + ":" + REFRESH_HIGH + ":" + REFRESH_EXTREME; + writeValue(value); + } + return value; + } + + protected void writePackage(String packageName, int mode) { + String value = getValue(); + value = value.replace(packageName + ",", ""); + String[] modes = value.split(":"); + String finalString; + + switch (mode) { + case STATE_STANDARD: + modes[0] = modes[0] + packageName + ","; + break; + case STATE_HIGH: + modes[1] = modes[1] + packageName + ","; + break; + case STATE_EXTREME: + modes[2] = modes[2] + packageName + ","; + break; + } + + finalString = modes[0] + ":" + modes[1] + ":" + modes[2]; + + writeValue(finalString); + } + + protected int getStateForPackage(String packageName) { + String value = getValue(); + String[] modes = value.split(":"); + int state = STATE_DEFAULT; + if (modes[0].contains(packageName + ",")) { + state = STATE_STANDARD; + } else if (modes[1].contains(packageName + ",")) { + state = STATE_HIGH; + } else if (modes[2].contains(packageName + ",")) { + state = STATE_EXTREME; + } + return state; + } + + protected void setRefreshRate(String packageName) { + String value = getValue(); + String modes[]; + float maxrate = defaultMaxRate; + float minrate = defaultMinRate; + isAppInList = false; + + if (value != null) { + modes = value.split(":"); + + if (modes[0].contains(packageName + ",")) { + maxrate = REFRESH_STATE_STANDARD; + if ( minrate > maxrate){ + minrate = maxrate; + } + isAppInList = true; + } else if (modes[1].contains(packageName + ",")) { + maxrate = REFRESH_STATE_HIGH; + if (minrate > maxrate) { + minrate = maxrate; + } + isAppInList = true; + } else if (modes[2].contains(packageName + ",")) { + maxrate = REFRESH_STATE_EXTREME; + if ( minrate > maxrate){ + minrate = maxrate; + } + isAppInList = true; + } + } + Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, minrate); + Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, maxrate); + } +} diff --git a/parts/src/org/lineageos/settings/saturation/SaturationActivity.java b/parts/src/org/lineageos/settings/saturation/SaturationActivity.java new file mode 100644 index 00000000..829a37db --- /dev/null +++ b/parts/src/org/lineageos/settings/saturation/SaturationActivity.java @@ -0,0 +1,34 @@ +/* + * 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.saturation; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class SaturationActivity extends CollapsingToolbarBaseActivity { + + private static final String TAG = "Saturation"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new SaturationFragment(), TAG).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/saturation/SaturationFragment.java b/parts/src/org/lineageos/settings/saturation/SaturationFragment.java new file mode 100644 index 00000000..8a5aa442 --- /dev/null +++ b/parts/src/org/lineageos/settings/saturation/SaturationFragment.java @@ -0,0 +1,267 @@ +/* + * 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.saturation; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.android.settingslib.widget.LayoutPreference; + +import org.lineageos.settings.Constants; +import org.lineageos.settings.CustomSeekBarPreference; +import org.lineageos.settings.R; +import org.lineageos.settings.utils.TileUtils; + +import java.util.Arrays; + +public class SaturationFragment extends PreferenceFragmentCompat + implements Preference.OnPreferenceChangeListener { + + private View mViewArrowPrevious; + private View mViewArrowNext; + private ViewPager mViewPager; + + private ImageView[] mDotIndicators; + private View[] mViewPagerImages; + + private CustomSeekBarPreference mSaturationPreference; + + private IBinder mSurfaceFlinger; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.saturation, rootKey); + setHasOptionsMenu(true); + + LayoutPreference preview = findPreference(Constants.KEY_SATURATION_PREVIEW); + addViewPager(preview); + + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + mSaturationPreference = (CustomSeekBarPreference) findPreference(Constants.KEY_SATURATION); + mSaturationPreference.setOnPreferenceChangeListener(this); + int seekBarValue = sharedPrefs.getInt(Constants.KEY_SATURATION, 100); + updateSaturation(seekBarValue); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.saturation_menu, menu); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mSaturationPreference) { + int seekBarValue = (Integer) newValue; + updateSaturation(seekBarValue); + return true; + } + return false; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.add_tile) { + TileUtils.requestAddTileService( + getContext(), + SaturationTileService.class, + R.string.saturation_title, + R.drawable.ic_saturation_tile + ); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + public void updateSaturation(int seekBarValue) { + float saturation; + if (seekBarValue == 100) { + saturation = 1.001f; + } else { + saturation = seekBarValue / 100.0f; + } + + if (mSurfaceFlinger != null) { + try { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeFloat(saturation); + mSurfaceFlinger.transact(1022, data, null, 0); + data.recycle(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + } + + public void restoreSaturationSetting(Context context) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + int seekBarValue = sharedPrefs.getInt(Constants.KEY_SATURATION, 100); + updateSaturation(seekBarValue); + } + + void addViewPager(LayoutPreference preview) { + mViewPager = preview.findViewById(R.id.viewpager); + + int[] drawables = new int[]{ + R.drawable.image_preview1, + R.drawable.image_preview2, + R.drawable.image_preview3 + }; + + mViewPagerImages = new View[drawables.length]; + + for (int idx = 0; idx < drawables.length; idx++) { + mViewPagerImages[idx] = getLayoutInflater().inflate(R.layout.image_layout, null); + ImageView imageView = mViewPagerImages[idx].findViewById(R.id.imageView); + imageView.setImageResource(drawables[idx]); + } + + mViewPager.setAdapter(new ImagePreviewPagerAdapter(mViewPagerImages)); + + mViewArrowPrevious = preview.findViewById(R.id.arrow_previous); + mViewArrowPrevious.setOnClickListener(v -> mViewPager.setCurrentItem(mViewPager.getCurrentItem() - 1, true)); + + mViewArrowNext = preview.findViewById(R.id.arrow_next); + mViewArrowNext.setOnClickListener(v -> mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true)); + + mViewPager.addOnPageChangeListener(createPageListener()); + + final ViewGroup viewGroup = preview.findViewById(R.id.viewGroup); + mDotIndicators = new ImageView[mViewPagerImages.length]; + for (int i = 0; i < mViewPagerImages.length; i++) { + final ImageView imageView = new ImageView(getContext()); + final ViewGroup.MarginLayoutParams lp = + new ViewGroup.MarginLayoutParams(12, 12); + lp.setMargins(6, 0, 6, 0); + imageView.setLayoutParams(lp); + mDotIndicators[i] = imageView; + + viewGroup.addView(mDotIndicators[i]); + } + + updateIndicator(mViewPager.getCurrentItem()); + } + + private ViewPager.OnPageChangeListener createPageListener() { + return new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled( + int position, float positionOffset, int positionOffsetPixels) { + if (positionOffset != 0) { + for (View mViewPagerImage : mViewPagerImages) { + mViewPagerImage.setVisibility(View.VISIBLE); + } + } else { + mViewPagerImages[position].setContentDescription( + getContext().getString(R.string.image_preview_content_description)); + updateIndicator(position); + } + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }; + } + + private void updateIndicator(int position) { + for (int i = 0; i < mViewPagerImages.length; i++) { + if (position == i) { + mDotIndicators[i].setBackgroundResource( + R.drawable.ic_image_preview_page_indicator_focused); + + mViewPagerImages[i].setVisibility(View.VISIBLE); + } else { + mDotIndicators[i].setBackgroundResource( + R.drawable.ic_image_preview_page_indicator_unfocused); + + mViewPagerImages[i].setVisibility(View.INVISIBLE); + } + } + + if (position == 0) { + mViewArrowPrevious.setVisibility(View.INVISIBLE); + mViewArrowNext.setVisibility(View.VISIBLE); + } else if (position == (mViewPagerImages.length - 1)) { + mViewArrowPrevious.setVisibility(View.VISIBLE); + mViewArrowNext.setVisibility(View.INVISIBLE); + } else { + mViewArrowPrevious.setVisibility(View.VISIBLE); + mViewArrowNext.setVisibility(View.VISIBLE); + } + } + + static class ImagePreviewPagerAdapter extends PagerAdapter { + private final View[] mPageViewList; + + ImagePreviewPagerAdapter(View[] pageViewList) { + mPageViewList = pageViewList; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + if (mPageViewList[position] != null) { + container.removeView(mPageViewList[position]); + } + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + container.addView(mPageViewList[position]); + return mPageViewList[position]; + } + + @Override + public int getCount() { + return mPageViewList.length; + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return object == view; + } + } +} diff --git a/parts/src/org/lineageos/settings/saturation/SaturationTileService.java b/parts/src/org/lineageos/settings/saturation/SaturationTileService.java new file mode 100644 index 00000000..81bd3a11 --- /dev/null +++ b/parts/src/org/lineageos/settings/saturation/SaturationTileService.java @@ -0,0 +1,51 @@ +/* + * 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.saturation; + +import android.app.PendingIntent; +import android.content.Intent; +import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; + +public class SaturationTileService extends TileService { + + @Override + public void onStartListening() { + super.onStartListening(); + updateTile(); + } + + @Override + public void onClick() { + super.onClick(); + Intent intent = new Intent(this, SaturationActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE + ); + startActivityAndCollapse(pendingIntent); + } + + private void updateTile() { + final Tile tile = getQsTile(); + tile.setState(Tile.STATE_ACTIVE); + tile.updateTile(); + } +} diff --git a/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsActivity.java b/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsActivity.java new file mode 100644 index 00000000..c85b22ae --- /dev/null +++ b/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsActivity.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2025 GuidixX + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.settings.soundcontrol; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class SoundControlSettingsActivity extends CollapsingToolbarBaseActivity { + private static final String TAG_SOUND_CONTROL = "soundcontrol"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getSupportFragmentManager().beginTransaction().replace( + com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new SoundControlSettingsFragment(), TAG_SOUND_CONTROL).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsFragment.java b/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsFragment.java new file mode 100644 index 00000000..442829b0 --- /dev/null +++ b/parts/src/org/lineageos/settings/soundcontrol/SoundControlSettingsFragment.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 GuidixX + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.settings.soundcontrol; + +import android.os.Bundle; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.SeekBarPreference; +import androidx.preference.SwitchPreferenceCompat; +import org.lineageos.settings.R; + +public class SoundControlSettingsFragment extends PreferenceFragmentCompat { + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.sound_control_settings, rootKey); + + SeekBarPreference micGainPref = findPreference("mic_gain"); + SeekBarPreference hpLeftGainPref = findPreference("hp_left_gain"); + SeekBarPreference hpRightGainPref = findPreference("hp_right_gain"); + SwitchPreferenceCompat enableSwitch = findPreference("sound_control_enable"); + + micGainPref.setValue(SoundControlUtils.getSavedMicGain(getContext())); + int[] hpGains = SoundControlUtils.getSavedHpGain(getContext()); + hpLeftGainPref.setValue(hpGains[0]); + hpRightGainPref.setValue(hpGains[1]); + + boolean enabled = SoundControlUtils.isEnabled(getContext()); + enableSwitch.setChecked(enabled); + micGainPref.setEnabled(enabled); + hpLeftGainPref.setEnabled(enabled); + hpRightGainPref.setEnabled(enabled); + + micGainPref.setOnPreferenceChangeListener((pref, newValue) -> { + SoundControlUtils.saveMicGain(getContext(), (int) newValue); + return true; + }); + hpLeftGainPref.setOnPreferenceChangeListener((pref, newValue) -> { + SoundControlUtils.saveHpGain(getContext(), (int) newValue, hpRightGainPref.getValue()); + return true; + }); + hpRightGainPref.setOnPreferenceChangeListener((pref, newValue) -> { + SoundControlUtils.saveHpGain(getContext(), hpLeftGainPref.getValue(), (int) newValue); + return true; + }); + enableSwitch.setOnPreferenceChangeListener((pref, newValue) -> { + boolean isEnabled = (Boolean) newValue; + SoundControlUtils.setEnabled(getContext(), isEnabled); + micGainPref.setEnabled(isEnabled); + hpLeftGainPref.setEnabled(isEnabled); + hpRightGainPref.setEnabled(isEnabled); + if (isEnabled) { + SoundControlUtils.applyAll(getContext()); + } + return true; + }); + } +} diff --git a/parts/src/org/lineageos/settings/soundcontrol/SoundControlUtils.java b/parts/src/org/lineageos/settings/soundcontrol/SoundControlUtils.java new file mode 100644 index 00000000..b3e76147 --- /dev/null +++ b/parts/src/org/lineageos/settings/soundcontrol/SoundControlUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2025 GuidixX + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.settings.soundcontrol; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import org.lineageos.settings.utils.FileUtils; + +public class SoundControlUtils { + private static final String PREF_MIC_GAIN = "sound_mic_gain"; + private static final String PREF_HP_LEFT_GAIN = "sound_hp_left_gain"; + private static final String PREF_HP_RIGHT_GAIN = "sound_hp_right_gain"; + private static final String MIC_GAIN_PATH = "/sys/kernel/sound_control/mic_gain"; + private static final String HP_GAIN_PATH = "/sys/kernel/sound_control/headphone_gain"; + private static final String PREF_ENABLE = "sound_control_enable"; + + public static void saveMicGain(Context ctx, int value) { + PreferenceManager.getDefaultSharedPreferences(ctx).edit().putInt(PREF_MIC_GAIN, value).apply(); + writeInt(MIC_GAIN_PATH, value); + } + public static void saveHpGain(Context ctx, int left, int right) { + PreferenceManager.getDefaultSharedPreferences(ctx) + .edit().putInt(PREF_HP_LEFT_GAIN, left).putInt(PREF_HP_RIGHT_GAIN, right).apply(); + writeHeadphoneGain(HP_GAIN_PATH, left, right); + } + public static int getSavedMicGain(Context ctx) { + return PreferenceManager.getDefaultSharedPreferences(ctx).getInt(PREF_MIC_GAIN, 0); + } + public static int[] getSavedHpGain(Context ctx) { + return new int[] { + PreferenceManager.getDefaultSharedPreferences(ctx).getInt(PREF_HP_LEFT_GAIN, 0), + PreferenceManager.getDefaultSharedPreferences(ctx).getInt(PREF_HP_RIGHT_GAIN, 0) + }; + } + public static void applyAll(Context ctx) { + writeInt(MIC_GAIN_PATH, getSavedMicGain(ctx)); + int[] hp = getSavedHpGain(ctx); + writeHeadphoneGain(HP_GAIN_PATH, hp[0], hp[1]); + } + public static int readInt(String path, int def) { + try { + return Integer.parseInt(FileUtils.readOneLine(path)); + } catch (Exception e) { + return def; + } + } + public static void writeInt(String path, int value) { + FileUtils.writeLine(path, String.valueOf(value)); + } + public static int[] readHeadphoneGain(String path) { + try { + String[] vals = FileUtils.readOneLine(path).split(" "); + return new int[]{Integer.parseInt(vals[0]), Integer.parseInt(vals[1])}; + } catch (Exception e) { + return new int[]{0, 0}; + } + } + public static void writeHeadphoneGain(String path, int left, int right) { + FileUtils.writeLine(path, left + " " + right); + } + + public static boolean isEnabled(Context ctx) { + return PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean(PREF_ENABLE, true); + } + public static void setEnabled(Context ctx, boolean enabled) { + PreferenceManager.getDefaultSharedPreferences(ctx).edit().putBoolean(PREF_ENABLE, enabled).apply(); + } +} diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java new file mode 100644 index 00000000..3a9b5636 --- /dev/null +++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.speaker; + +import android.os.Bundle; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class ClearSpeakerActivity extends CollapsingToolbarBaseActivity { + + private static final String TAG_CLEARSPEAKER = "clearspeaker"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new ClearSpeakerFragment(), TAG_CLEARSPEAKER).commit(); + } +} diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java new file mode 100644 index 00000000..7242bc16 --- /dev/null +++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java @@ -0,0 +1,111 @@ +/* + * 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. + */ + +package org.lineageos.settings.speaker; + +import android.content.res.AssetFileDescriptor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import androidx.preference.Preference; +import androidx.preference.PreferenceFragment; +import androidx.preference.SwitchPreference; + +import org.lineageos.settings.R; + +import java.io.IOException; + +public class ClearSpeakerFragment extends PreferenceFragment implements + Preference.OnPreferenceChangeListener { + + private static final String TAG = "ClearSpeakerFragment"; + private static final String PREF_CLEAR_SPEAKER = "clear_speaker_pref"; + private static final int PLAY_DURATION_MS = 30000; + + private Handler mHandler = new Handler(Looper.getMainLooper()); + private MediaPlayer mMediaPlayer; + private SwitchPreference mClearSpeakerPref; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.clear_speaker_settings); + + mClearSpeakerPref = findPreference(PREF_CLEAR_SPEAKER); + mClearSpeakerPref.setOnPreferenceChangeListener(this); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mClearSpeakerPref) { + boolean value = (Boolean) newValue; + if (value && startPlaying()) { + mHandler.removeCallbacksAndMessages(null); + mHandler.postDelayed(this::stopPlaying, PLAY_DURATION_MS); + return true; + } + } + return false; + } + + @Override + public void onStop() { + super.onStop(); + stopPlaying(); + } + + public boolean startPlaying() { + getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build()); + mMediaPlayer.setLooping(true); + try (AssetFileDescriptor afd = getResources().openRawResourceFd( + R.raw.clear_speaker_sound)) { + mMediaPlayer.setDataSource(afd); + mMediaPlayer.setVolume(1.0f, 1.0f); + mMediaPlayer.prepare(); + mMediaPlayer.start(); + mClearSpeakerPref.setEnabled(false); + } catch (IOException | IllegalArgumentException e) { + Log.e(TAG, "Failed to play speaker clean sound!", e); + return false; + } + return true; + } + + public void stopPlaying() { + if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { + try { + mMediaPlayer.stop(); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to stop media player!", e); + } finally { + mMediaPlayer.reset(); + mMediaPlayer.release(); + mMediaPlayer=null; + } + } + mClearSpeakerPref.setEnabled(true); + mClearSpeakerPref.setChecked(false); + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalActivity.java b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java new file mode 100644 index 00000000..e127c354 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.thermal; + +import android.os.Bundle; +import android.view.MenuItem; + +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class ThermalActivity extends CollapsingToolbarBaseActivity { + + private static final String TAG_THERMAL = "thermal"; + private static final String THERMAL_SCONFIG = "/sys/class/thermal/thermal_message/sconfig"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, + new ThermalSettingsFragment(), TAG_THERMAL).commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; + } + return false; + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalService.java b/parts/src/org/lineageos/settings/thermal/ThermalService.java new file mode 100644 index 00000000..4e29a560 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalService.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.thermal; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ActivityTaskManager.RootTaskInfo; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; +import android.app.Service; +import android.app.TaskStackListener; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +public class ThermalService extends Service { + + private static final String TAG = "ThermalService"; + private static final boolean DEBUG = false; + + private boolean mScreenOn = true; + private String mCurrentApp = ""; + private ThermalUtils mThermalUtils; + + private IActivityTaskManager mActivityTaskManager; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case Intent.ACTION_SCREEN_OFF: + mScreenOn = false; + setThermalProfile(); + break; + case Intent.ACTION_SCREEN_ON: + mScreenOn = true; + setThermalProfile(); + break; + } + } + }; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "Creating service"); + try { + mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager.registerTaskStackListener(mTaskListener); + } catch (RemoteException e) { + // Do nothing + } + mThermalUtils = new ThermalUtils(this); + registerReceiver(); + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) Log.d(TAG, "Starting service"); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void registerReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + this.registerReceiver(mIntentReceiver, filter); + } + + private void setThermalProfile() { + if (mScreenOn) { + mThermalUtils.setThermalProfile(mCurrentApp); + } else { + mThermalUtils.setDefaultThermalProfile(); + } + } + + private final TaskStackListener mTaskListener = new TaskStackListener() { + @Override + public void onTaskStackChanged() { + try { + final ActivityTaskManager.RootTaskInfo focusedTask = + ActivityTaskManager.getService().getFocusedRootTaskInfo(); + if (focusedTask != null && focusedTask.topActivity != null) { + ComponentName taskComponentName = focusedTask.topActivity; + String foregroundApp = taskComponentName.getPackageName(); + if (!foregroundApp.equals(mCurrentApp)) { + mCurrentApp = foregroundApp; + setThermalProfile(); + } + } + } catch (Exception e) {} + } + }; +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java new file mode 100644 index 00000000..d30d9845 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java @@ -0,0 +1,432 @@ +/** + * Copyright (C) 2020 The LineageOS Project + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.lineageos.settings.thermal; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceFragment; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.settingslib.applications.ApplicationsState; + +import org.lineageos.settings.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ThermalSettingsFragment extends PreferenceFragment + implements ApplicationsState.Callbacks { + + private AllPackagesAdapter mAllPackagesAdapter; + private ApplicationsState mApplicationsState; + private ApplicationsState.Session mSession; + private ActivityFilter mActivityFilter; + private Map mEntryMap = + new HashMap(); + + private ThermalUtils mThermalUtils; + private RecyclerView mAppsRecyclerView; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mApplicationsState.newSession(this); + mSession.onResume(); + mActivityFilter = new ActivityFilter(getActivity().getPackageManager()); + + mAllPackagesAdapter = new AllPackagesAdapter(getActivity()); + + mThermalUtils = new ThermalUtils(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.thermal_layout, container, false); + } + + @Override + public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAppsRecyclerView = view.findViewById(R.id.thermal_rv_view); + mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + mAppsRecyclerView.setAdapter(mAllPackagesAdapter); + } + + + @Override + public void onResume() { + super.onResume(); + getActivity().setTitle(getResources().getString(R.string.thermal_title)); + rebuild(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mSession.onPause(); + mSession.onDestroy(); + } + + @Override + public void onPackageListChanged() { + mActivityFilter.updateLauncherInfoList(); + rebuild(); + } + + @Override + public void onRebuildComplete(ArrayList entries) { + if (entries != null) { + handleAppEntries(entries); + mAllPackagesAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onLoadEntriesCompleted() { + rebuild(); + } + + @Override + public void onAllSizesComputed() { + } + + @Override + public void onLauncherInfoChanged() { + } + + @Override + public void onPackageIconChanged() { + } + + @Override + public void onPackageSizeChanged(String packageName) { + } + + @Override + public void onRunningStateChanged(boolean running) { + } + + private void handleAppEntries(List entries) { + final ArrayList sections = new ArrayList(); + final ArrayList positions = new ArrayList(); + final PackageManager pm = getActivity().getPackageManager(); + String lastSectionIndex = null; + int offset = 0; + + for (int i = 0; i < entries.size(); i++) { + final ApplicationInfo info = entries.get(i).info; + final String label = (String) info.loadLabel(pm); + final String sectionIndex; + + if (!info.enabled) { + sectionIndex = "--"; // XXX + } else if (TextUtils.isEmpty(label)) { + sectionIndex = ""; + } else { + sectionIndex = label.substring(0, 1).toUpperCase(); + } + + if (lastSectionIndex == null || + !TextUtils.equals(sectionIndex, lastSectionIndex)) { + sections.add(sectionIndex); + positions.add(offset); + lastSectionIndex = sectionIndex; + } + + offset++; + } + + mAllPackagesAdapter.setEntries(entries, sections, positions); + mEntryMap.clear(); + for (ApplicationsState.AppEntry e : entries) { + mEntryMap.put(e.info.packageName, e); + } + } + + private void rebuild() { + mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR); + } + + private int getStateDrawable(int state) { + switch (state) { + case ThermalUtils.STATE_BENCHMARK: + return R.drawable.ic_thermal_benchmark; + case ThermalUtils.STATE_BROWSER: + return R.drawable.ic_thermal_browser; + case ThermalUtils.STATE_CAMERA: + return R.drawable.ic_thermal_camera; + case ThermalUtils.STATE_DIALER: + return R.drawable.ic_thermal_dialer; + case ThermalUtils.STATE_GAMING: + return R.drawable.ic_thermal_gaming; + case ThermalUtils.STATE_STREAMING: + return R.drawable.ic_thermal_streaming; + case ThermalUtils.STATE_DEFAULT: + default: + return R.drawable.ic_thermal_default; + } + } + + private class ViewHolder extends RecyclerView.ViewHolder { + private TextView title; + private Spinner mode; + private ImageView icon; + private View rootView; + private ImageView stateIcon; + + private ViewHolder(View view) { + super(view); + this.title = view.findViewById(R.id.app_name); + this.mode = view.findViewById(R.id.app_mode); + this.icon = view.findViewById(R.id.app_icon); + this.stateIcon = view.findViewById(R.id.state); + this.rootView = view; + + view.setTag(this); + } + } + + private class ModeAdapter extends BaseAdapter { + + private final LayoutInflater inflater; + private final int[] items = { + R.string.thermal_default, + R.string.thermal_benchmark, + R.string.thermal_browser, + R.string.thermal_camera, + R.string.thermal_dialer, + R.string.thermal_gaming, + R.string.thermal_streaming + }; + + private ModeAdapter(Context context) { + inflater = LayoutInflater.from(context); + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Object getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView view; + if (convertView != null) { + view = (TextView) convertView; + } else { + view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item, + parent, false); + } + + view.setText(items[position]); + view.setTextSize(14f); + + return view; + } + } + + private class AllPackagesAdapter extends RecyclerView.Adapter + implements AdapterView.OnItemSelectedListener, SectionIndexer { + + private List mEntries = new ArrayList<>(); + private String[] mSections; + private int[] mPositions; + + public AllPackagesAdapter(Context context) { + mActivityFilter = new ActivityFilter(context.getPackageManager()); + } + + @Override + public int getItemCount() { + return mEntries.size(); + } + + @Override + public long getItemId(int position) { + return mEntries.get(position).id; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + ViewHolder holder = new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.thermal_list_item, parent, false)); + Context context = holder.itemView.getContext(); + holder.mode.setAdapter(new ModeAdapter(context)); + holder.mode.setOnItemSelectedListener(this); + return holder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + ApplicationsState.AppEntry entry = mEntries.get(position); + + if (entry == null) { + return; + } + + holder.title.setText(entry.label); + holder.title.setOnClickListener(v -> holder.mode.performClick()); + mApplicationsState.ensureIcon(entry); + holder.icon.setImageDrawable(entry.icon); + int packageState = mThermalUtils.getStateForPackage(entry.info.packageName); + holder.mode.setSelection(packageState, false); + holder.mode.setTag(entry); + holder.stateIcon.setImageResource(getStateDrawable(packageState)); + } + + private void setEntries(List entries, + List sections, List positions) { + mEntries = entries; + mSections = sections.toArray(new String[sections.size()]); + mPositions = new int[positions.size()]; + for (int i = 0; i < positions.size(); i++) { + mPositions[i] = positions.get(i); + } + notifyDataSetChanged(); + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag(); + int currentState = mThermalUtils.getStateForPackage(entry.info.packageName); + if (currentState != position) { + mThermalUtils.writePackage(entry.info.packageName, position); + notifyDataSetChanged(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + @Override + public int getPositionForSection(int section) { + if (section < 0 || section >= mSections.length) { + return -1; + } + + return mPositions[section]; + } + + @Override + public int getSectionForPosition(int position) { + if (position < 0 || position >= getItemCount()) { + return -1; + } + + final int index = Arrays.binarySearch(mPositions, position); + + /* + * Consider this example: section positions are 0, 3, 5; the supplied + * position is 4. The section corresponding to position 4 starts at + * position 3, so the expected return value is 1. Binary search will not + * find 4 in the array and thus will return -insertPosition-1, i.e. -3. + * To get from that number to the expected value of 1 we need to negate + * and subtract 2. + */ + return index >= 0 ? index : -index - 2; + } + + @Override + public Object[] getSections() { + return mSections; + } + } + + private class ActivityFilter implements ApplicationsState.AppFilter { + + private final PackageManager mPackageManager; + private final List mLauncherResolveInfoList = new ArrayList(); + + private ActivityFilter(PackageManager packageManager) { + this.mPackageManager = packageManager; + + updateLauncherInfoList(); + } + + public void updateLauncherInfoList() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfoList = mPackageManager.queryIntentActivities(i, 0); + + synchronized (mLauncherResolveInfoList) { + mLauncherResolveInfoList.clear(); + for (ResolveInfo ri : resolveInfoList) { + mLauncherResolveInfoList.add(ri.activityInfo.packageName); + } + } + } + + @Override + public void init() { + } + + @Override + public boolean filterApp(ApplicationsState.AppEntry entry) { + boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName); + if (show) { + synchronized (mLauncherResolveInfoList) { + show = mLauncherResolveInfoList.contains(entry.info.packageName); + } + } + return show; + } + } +} diff --git a/parts/src/org/lineageos/settings/thermal/ThermalUtils.java b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java new file mode 100644 index 00000000..cf71fbe2 --- /dev/null +++ b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.settings.thermal; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.UserHandle; + +import androidx.preference.PreferenceManager; + +import org.lineageos.settings.utils.FileUtils; + +public final class ThermalUtils { + + private static final String THERMAL_CONTROL = "thermal_control"; + + protected static final int STATE_DEFAULT = 0; + protected static final int STATE_BENCHMARK = 1; + protected static final int STATE_BROWSER = 2; + protected static final int STATE_CAMERA = 3; + protected static final int STATE_DIALER = 4; + protected static final int STATE_GAMING = 5; + protected static final int STATE_STREAMING = 6; + + private static final String THERMAL_STATE_DEFAULT = "0"; + private static final String THERMAL_STATE_BENCHMARK = "10"; + private static final String THERMAL_STATE_BROWSER = "11"; + private static final String THERMAL_STATE_CAMERA = "12"; + private static final String THERMAL_STATE_DIALER = "8"; + private static final String THERMAL_STATE_GAMING = "9"; + private static final String THERMAL_STATE_STREAMING = "14"; + + private static final String THERMAL_BENCHMARK = "thermal.benchmark="; + private static final String THERMAL_BROWSER = "thermal.browser="; + private static final String THERMAL_CAMERA = "thermal.camera="; + private static final String THERMAL_DIALER = "thermal.dialer="; + private static final String THERMAL_GAMING = "thermal.gaming="; + private static final String THERMAL_STREAMING = "thermal.streaming="; + + private static final String THERMAL_SCONFIG = "/sys/class/thermal/thermal_message/sconfig"; + + private SharedPreferences mSharedPrefs; + + protected ThermalUtils(Context context) { + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public static void startService(Context context) { + context.startServiceAsUser(new Intent(context, ThermalService.class), + UserHandle.CURRENT); + } + + private void writeValue(String profiles) { + mSharedPrefs.edit().putString(THERMAL_CONTROL, profiles).apply(); + } + + private String getValue() { + String value = mSharedPrefs.getString(THERMAL_CONTROL, null); + + if (value == null || value.isEmpty()) { + value = THERMAL_BENCHMARK + ":" + THERMAL_BROWSER + ":" + THERMAL_CAMERA + ":" + + THERMAL_DIALER + ":" + THERMAL_GAMING + ":" + THERMAL_STREAMING; + writeValue(value); + } + return value; + } + + protected void writePackage(String packageName, int mode) { + String value = getValue(); + value = value.replace(packageName + ",", ""); + String[] modes = value.split(":"); + String finalString; + + switch (mode) { + case STATE_BENCHMARK: + modes[0] = modes[0] + packageName + ","; + break; + case STATE_BROWSER: + modes[1] = modes[1] + packageName + ","; + break; + case STATE_CAMERA: + modes[2] = modes[2] + packageName + ","; + break; + case STATE_DIALER: + modes[3] = modes[3] + packageName + ","; + break; + case STATE_GAMING: + modes[4] = modes[4] + packageName + ","; + break; + case STATE_STREAMING: + modes[5] = modes[5] + packageName + ","; + break; + } + + finalString = modes[0] + ":" + modes[1] + ":" + modes[2] + ":" + modes[3] + ":" + + modes[4] + ":" + modes[5]; + + writeValue(finalString); + } + + protected int getStateForPackage(String packageName) { + String value = getValue(); + String[] modes = value.split(":"); + int state = STATE_DEFAULT; + if (modes[0].contains(packageName + ",")) { + state = STATE_BENCHMARK; + } else if (modes[1].contains(packageName + ",")) { + state = STATE_BROWSER; + } else if (modes[2].contains(packageName + ",")) { + state = STATE_CAMERA; + } else if (modes[3].contains(packageName + ",")) { + state = STATE_DIALER; + } else if (modes[4].contains(packageName + ",")) { + state = STATE_GAMING; + } else if (modes[5].contains(packageName + ",")) { + state = STATE_STREAMING; + } + + return state; + } + + protected void setDefaultThermalProfile() { + FileUtils.writeLine(THERMAL_SCONFIG, THERMAL_STATE_DEFAULT); + } + + protected void setThermalProfile(String packageName) { + String value = getValue(); + String modes[]; + String state = THERMAL_STATE_DEFAULT; + + if (value != null) { + modes = value.split(":"); + + if (modes[0].contains(packageName + ",")) { + state = THERMAL_STATE_BENCHMARK; + } else if (modes[1].contains(packageName + ",")) { + state = THERMAL_STATE_BROWSER; + } else if (modes[2].contains(packageName + ",")) { + state = THERMAL_STATE_CAMERA; + } else if (modes[3].contains(packageName + ",")) { + state = THERMAL_STATE_DIALER; + } else if (modes[4].contains(packageName + ",")) { + state = THERMAL_STATE_GAMING; + } else if (modes[5].contains(packageName + ",")) { + state = THERMAL_STATE_STREAMING; + } + } + FileUtils.writeLine(THERMAL_SCONFIG, state); + } +} diff --git a/parts/src/org/lineageos/settings/utils/ComponentUtils.java b/parts/src/org/lineageos/settings/utils/ComponentUtils.java new file mode 100644 index 00000000..28701b53 --- /dev/null +++ b/parts/src/org/lineageos/settings/utils/ComponentUtils.java @@ -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 + ); + } + } +} diff --git a/parts/src/org/lineageos/settings/utils/FileUtils.java b/parts/src/org/lineageos/settings/utils/FileUtils.java new file mode 100644 index 00000000..b2bc40dc --- /dev/null +++ b/parts/src/org/lineageos/settings/utils/FileUtils.java @@ -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; + } +} + + diff --git a/parts/src/org/lineageos/settings/utils/TileUtils.java b/parts/src/org/lineageos/settings/utils/TileUtils.java new file mode 100644 index 00000000..b3feff49 --- /dev/null +++ b/parts/src/org/lineageos/settings/utils/TileUtils.java @@ -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; + } + } +} diff --git a/proprietary-files.txt b/proprietary-files.txt index 9e446bb5..93607d5c 100644 --- a/proprietary-files.txt +++ b/proprietary-files.txt @@ -1,4 +1,4 @@ -# All unpinned blobs below are extracted from garnet OS2.0.203.0.VNRMIXM +# All unpinned blobs below are extracted from garnet OS2.0.206.0.VNRMIXM # ACDB vendor/etc/acdbdata/Mise/Mise_acdb_cal.acdb @@ -35,7 +35,7 @@ vendor/lib/rfsa/adsp/libCalculator_skel.so vendor/lib/rfsa/adsp/libMIAIHDR_skel.so vendor/lib/rfsa/adsp/libQnnHtpV69.so vendor/lib/rfsa/adsp/libQnnHtpV69Skel.so -vendor/lib/rfsa/adsp/libQnnSaver.so +vendor/lib/rfsa/adsp/libQnnSaver.so;MODULE_SUFFIX=_adsp vendor/lib/rfsa/adsp/libSnpeDspV65Skel.so vendor/lib/rfsa/adsp/libSnpeDspV66Skel.so vendor/lib/rfsa/adsp/libSnpeHtpV68Skel.so @@ -232,35 +232,6 @@ vendor/lib64/vendor.qti.latency@2.1.so # Camera odm/etc/camera/camerabooster.json odm/etc/camera/camerascene.json -odm/etc/camera/videofilter/110_BlackGold.png -odm/etc/camera/videofilter/129_LeicaVivid.png -odm/etc/camera/videofilter/130_LeicaNatural.png -odm/etc/camera/videofilter/131_MonochromeTrue.png -odm/etc/camera/videofilter/132_MonochromeHighContrast.png -odm/etc/camera/videofilter/133_MonochromeBrown.png -odm/etc/camera/videofilter/134_MonochromeBlue.png -odm/etc/camera/videofilter/144_RedHero.png -odm/etc/camera/videofilter/146_TactileGreen.png -odm/etc/camera/videofilter/148_Mild.png -odm/etc/camera/videofilter/149_LilyWhite.png -odm/etc/camera/videofilter/150_Bright.png -odm/etc/camera/videofilter/151_Fresh.png -odm/etc/camera/videofilter/152_Limpid.png -odm/etc/camera/videofilter/153_KC64.png -odm/etc/camera/videofilter/154_V250.png -odm/etc/camera/videofilter/155_H400.png -odm/etc/camera/videofilter/156_ColdWhite.png -odm/etc/camera/videofilter/157_Native.png -odm/etc/camera/videofilter/158_BWClassical.png -odm/etc/camera/videofilter/159_Flowers.png -odm/etc/camera/videofilter/160_Vivid.png -odm/etc/camera/videofilter/161_KP160.png -odm/etc/camera/videofilter/162_Natural.png -odm/etc/camera/videofilter/163_KG200.png -odm/etc/camera/videofilter/164_FC400.png -odm/etc/camera/videofilter/165_C50D.png -odm/etc/camera/videofilter/166_F50.png -odm/lib64/libMiVideoFilter.so odm/lib64/librfs_warp.so vendor/bin/hw/vendor.qti.camera.provider@2.7-service_64 vendor/bin/hw/vendor.xiaomi.hardware.quickcamera@1.0-service @@ -652,7 +623,6 @@ vendor/lib64/camera/components/com.mi.node.superiq.so vendor/lib64/camera/components/com.mi.node.test_rearvideo.so vendor/lib64/camera/components/com.mi.node.tsskinbeautifier.so vendor/lib64/camera/components/com.mi.node.videobokeh.so -vendor/lib64/camera/components/com.mi.node.videofilter.so vendor/lib64/camera/components/com.qti.camx.chiiqutils.so vendor/lib64/camera/components/com.qti.eisv2.so vendor/lib64/camera/components/com.qti.eisv3.so @@ -1048,6 +1018,34 @@ vendor/lib64/camera/com.qti.tuned.garnet_sunny_s5khp3_wide_global_i.bin vendor/lib64/camera/com.qti.tuned.garnet_sunny_s5khp3_wide_i.bin vendor/lib64/hw/com.qti.chi.override.so +# Camera Video Filter LUts +vendor/etc/camera/102_SummerDay.png|854075e4570bbf561063a096dae0fe9d2151809d +vendor/etc/camera/103_Fantasy.png|0eb52c60f767c4a9b26b22f4ee35ec646545a123 +vendor/etc/camera/104_Meet.png|f72dbf0c125237acb41a115dab40233e275fcf92 +vendor/etc/camera/105_WindSing.png|8d07722a63e5619fbd3b293078ae67dfe271a72c +vendor/etc/camera/106_Lost.png|61a35b9eb3c6286357ba213cfce0b3e1fc112fd6 +vendor/etc/camera/107_Central.png|64c1e291c873ff7715ee9017c3a0d6cae48a9b6a +vendor/etc/camera/108_NorthernEurope.png|ae5c9c7ad2e37d4e924bac229af1577be8b2595b +vendor/etc/camera/109_Rome.png|6a46690036b8e6925b51b1b4c2a687870e486b87 +vendor/etc/camera/110_BlackGold.png|41f2757306323470f32c3a07356362e6903b7b02 +vendor/etc/camera/111_GreenOrange.png|7ccb8c48068296275ab440129750cddc47e26f4d +vendor/etc/camera/112_CyberPunk.png|a8245aafb5cf4a2bf2aa1ed58ad35dc3140607bb +vendor/etc/camera/113_BlackIce.png|00bb1b9b499aca960475b79414b5a076324c8b46 +vendor/etc/camera/121_Bleach.png|7cae58047cf3bd9854510cd54cd11c6c0e442f75 +vendor/etc/camera/122_Thriller.png|d73f8b11a3f45a0a646eb5d71b6a1d5bf72e90d3 +vendor/etc/camera/123_TealOrange.png|c7acfd64e602235ef26c0051cc125b15a2d6437b +vendor/etc/camera/124_JapaneseStyle.png|7dfec84eb0a7728e2503a7d260634e0f26011364 +vendor/etc/camera/125_Latin.png|eac04636de0aeac2480a3be9d1f0e04f58ddfd0b +vendor/etc/camera/126_Fantasy2.png|8b94fd32b66b3e88129618612f2603b9bdc0b3c9 +vendor/etc/camera/127_Creamy.png|71d1a5bc75edad2eb29ef58b1e41a18c55e5f609 +vendor/etc/camera/128_Sepia.png|ac5cf27142b07425c0775e8939ae2d99524ed622 +vendor/etc/camera/129_Vivid.png|5da99b1616af1d61a62c9e296a5b2e51951e1769 +vendor/etc/camera/130_Natural.png|e6b9363ca508adc198681356ad343c1e29bc695a + +# Camera Video Filter Libraries +vendor/lib64/libMiVideoFilter.so|7a7c64e8bbb4268414bc491ec3668132d60bfdbc|ab5942405e6129c10c42b5c599db610791daa5c2 +vendor/lib64/camera/components/com.mi.node.videofilter.so|503b5bcdc2c0be330c83b5823f54b2a0489f927e + # Computer vision vendor/lib64/libcv_common.so vendor/lib64/libcvp2_1.so @@ -1192,12 +1190,12 @@ vendor/bin/qdcmss|727147cbaf4d74f0eb38c534fda9beebdfec31f6 vendor/etc/init/qdcmss.rc|53d78d686678c4f923a9ec4ace7d4d3575b13788 vendor/lib64/libbacklight-calib.so|a5990fa87932196f9e2c38b2ec85786daa6b3a9c vendor/lib64/libdigital-dimming.so|66506a386dc80da0994c9d4aeac34edfc1830c5c -vendor/lib64/libdpps.so|d8e9cc561a3ab706697880975413db23bb41640c +vendor/lib64/libdpps.so|d8e9cc561a3ab706697880975413db23bb41640c|5d3e5f10ce5adff610e67acd837b19a551412876 vendor/lib64/libmemutils.so|e122c3ff95b10a6263e698261abafe62cee98010 vendor/lib64/libqdcm-algo.so|e873dc21878d92d20f6937453c9d3265a0d3a93d vendor/lib64/libqdcm-json-mode-parser.so|1c8f1a841a877efbed0a0b3db3d551ff1283f14f vendor/lib64/libqdcm-mode-parser.so|341178dfb18e03b6fabff27a080fb96f15188942 -vendor/lib64/libsnapdragoncolor-manager.so|72291478bd56bada0fc3df3617e765b28ef54f66 +vendor/lib64/libsnapdragoncolor-manager.so|72291478bd56bada0fc3df3617e765b28ef54f66|ba9a38c02dae4cc2211895714969c589faaff1f4 vendor/lib64/libsnapdragoncolor-qdcm.so|9f0bc72290618324e95cd9f4eca6987a6733bd06 # Display SDM - from ingot-user 14 UKQ1.240227.165 release-keys @@ -1356,6 +1354,9 @@ vendor/etc/init/android.hardware.gatekeeper@1.0-service-qti.rc vendor/lib64/hw/android.hardware.gatekeeper@1.0-impl-qti.so # Graphics (Adreno) +vendor/etc/permissions/android.hardware.vulkan.version-1_3.xml:vendor/etc/permissions/android.hardware.vulkan.version-1_3.xml +vendor/etc/permissions/android.software.opengles.deqp.level.xml +vendor/etc/permissions/android.software.vulkan.deqp.level.xml vendor/gpu/kbc/sequence_manifest.bin vendor/gpu/kbc/unified_kbcs_32.bin vendor/gpu/kbc/unified_kbcs_64.bin @@ -1367,6 +1368,9 @@ vendor/lib/egl/libGLESv2_adreno.so;SYMLINK=vendor/lib/libGLESv2_adreno.so vendor/lib/egl/libVkLayer_ADRENO_qprofiler.so vendor/lib/egl/libq3dtools_adreno.so;SYMLINK=vendor/lib/libq3dtools_adreno.so vendor/lib/egl/libq3dtools_esx.so +vendor/lib/libOpenCL_adreno.so +vendor/lib/libadreno_compiler_cl.so +vendor/lib/libdmabufheap.so vendor/lib/libCB.so vendor/lib/libOpenCL.so vendor/lib/libadreno_app_profiles.so @@ -1385,6 +1389,7 @@ vendor/lib64/egl/libGLESv2_adreno.so;SYMLINK=vendor/lib64/libGLESv2_adreno.so vendor/lib64/egl/libVkLayer_ADRENO_qprofiler.so vendor/lib64/egl/libq3dtools_adreno.so;SYMLINK=vendor/lib64/libq3dtools_adreno.so vendor/lib64/egl/libq3dtools_esx.so +vendor/lib64/egl/libPipeline_plugin.so vendor/lib64/libCB.so vendor/lib64/libOpenCL.so vendor/lib64/libadreno_app_profiles.so @@ -1396,6 +1401,10 @@ vendor/lib64/libkernelmanager.so vendor/lib64/libllvm-glnext.so vendor/lib64/libllvm-qcom.so vendor/lib64/libllvm-qgl.so +vendor/lib64/libOpenCL_adreno.so +vendor/lib64/libadreno_compiler_cl.so +vendor/lib64/libdmabufheap.so +vendor/lib64/libgamepoweroptfeature.so # Graphics (Adreno firmware) vendor/firmware/a710_sqe.fw @@ -1406,9 +1415,15 @@ vendor/firmware/a710_zap.elf vendor/firmware/a710_zap.mbn vendor/firmware/a710_zap.mdt vendor/firmware/gmu_gen70000.bin +vendor/firmware/a630_sqe.fw +vendor/firmware/a650_sqe.fw +vendor/firmware/a660_sqe.fw +vendor/firmware/a730_sqe.fw +vendor/firmware/gen71100_sqe.fw # Graphics (Vulkan) vendor/lib64/hw/vulkan.adreno.so +vendor/lib/hw/vulkan.adreno.so # Hotword enrollment product/etc/permissions/privapp-permissions-google-audio-system.xml:product/etc/permissions/privapp-permissions-hotword.xml @@ -1519,7 +1534,32 @@ vendor/lib64/libqcodec2_v4l2codec.so vendor/lib64/libvideotxr.so vendor/lib64/qti.video.utils.videobufferlayout.so +# Media (Dolby) +vendor/etc/dolby/dax-default.xml + +# Media (Dolby) - YUDI_OS1.0.2.0.UMHCNXM +vendor/etc/init/vendor.dolby.hardware.dms@2.0-service.rc|29296ebfe89b2697fec573be73cc3757fd97bed6 +vendor/etc/init/vendor.dolby.media.c2@1.0-service.rc|bc1e134139c7dd0e5b4f98952aa51c629ea66dc7 +vendor/etc/media_codecs_dolby_audio.xml|3119c61cc5471bb7902ac8b1ccd5785bb55a1918 +vendor/etc/vintf/manifest/vendor.dolby.hardware.dms.xml|c8095f649de19b80271fb429b4b6b439297a0678 +vendor/bin/hw/vendor.dolby.hardware.dms@2.0-service|556fa97b7fc5a3a8efd89aa413a4e6b508a9425e|f2bdf53f4d9ccc269a0df097e4b0acc7f3badb89 +vendor/bin/hw/vendor.dolby.media.c2@1.0-service|27e7aed8b219b082a66b8150e018f1db1c2bbcd8|e2afe22b6633f0b19fb39830c82c871cc0bc4d60 +vendor/lib64/libcodec2_soft_ac4dec.so|3f7da3e48b391f94e3e4aedbbea514eb5f4283ed +vendor/lib64/libcodec2_soft_ddpdec.so|55275a7abcfde2d3f44fc48f9dd7a976fb98ebc1 +vendor/lib64/libcodec2_store_dolby.so|d0a85979f9c622cc5ad4071635271fe51c9ecb55 +vendor/lib64/libdapparamstorage.so|6dd6cf3bd92710cdba1cb981b52e64dca2407bd1 +vendor/lib64/libdeccfg.so|8ef3036d0b7a5f2caae277e21b5cf5174b2f085f +vendor/lib64/libdlbdsservice.so|d4f9aa59644b1f90b221f188fc25a0b54db84c39|905fa82fbb586ff70db2c665546f01d4844e481c +vendor/lib64/libdlbpreg.so|e1588ec15d1b328c037ffd2f80c426dcd90d6467|464e56435f739fc4f7a1f953e19feaeec7a06850 +vendor/lib64/soundfx/libdlbvol.so|1ebd52ecc1a42e1ec0eb785e541b08d191bf9924|56a4fbd854097e4b3b151e9ad94056699e55e623 +vendor/lib64/soundfx/libhwdap.so|1058720b5930196370e1e792a14e60e16c9a46e1|42dcf6149366ff1e8f3679d771bdcb14bb62a599 +vendor/lib64/soundfx/libswgamedap.so|cac12e23c860270775986d56c8f9b8f58ac07be3 +vendor/lib64/soundfx/libswvqe.so|bcfc527b34066600c4b747cced45ab8012e4c1a5 +vendor/lib64/vendor.dolby.hardware.dms@2.0-impl.so|63db226e3f51448c094ef00ec9a92be089050219 +vendor/lib64/vendor.dolby.hardware.dms@2.0.so|e5c3409c9422297055cc79eff526e625be79ac32 + # Media Config +vendor/etc/media_codecs_c2_audio.xml vendor/etc/media_codecs_parrot_v0.xml vendor/etc/media_codecs_performance_parrot_v0.xml vendor/etc/media_parrot_v0/video_system_specs.json @@ -1533,11 +1573,6 @@ vendor/etc/vintf/manifest/manifest_vendor.xiaomi.hardware.mlipay.xml vendor/lib64/libmlipay.so vendor/lib64/libmlipay@1.1.so -# Modem manager -vendor/bin/modemManager -vendor/etc/init/modemManager.rc -vendor/etc/seccomp_policy/modemManager.policy - # NFC vendor/etc/libnfc-nci.conf vendor/etc/libnfc-nxp.conf @@ -1625,25 +1660,6 @@ vendor/lib64/libsubsystem_control.so vendor/bin/init.qti.qcv.sh vendor/etc/init/init.qti.qcv.rc -# QESDK -vendor/bin/qesdk-manager -vendor/bin/sensors-qesdk -vendor/etc/init/qesdk-manager.rc -vendor/etc/init/vendor.sensors.qesdk.rc -vendor/etc/seccomp_policy/qesdk.policy -vendor/etc/seccomp_policy/sensors-qesdk.policy -vendor/lib64/libqesdk.so -vendor/lib64/libqesdk_manager.so -vendor/lib64/vendor.qti.qesdhal@1.0.so -vendor/lib64/vendor.qti.qesdhal@1.1-impl.so -vendor/lib64/vendor.qti.qesdhal@1.1.so -vendor/lib64/vendor.qti.qesdsys-V1-ndk_platform.so - -# QGuard -vendor/bin/qguard -vendor/etc/init/qguard.rc -vendor/etc/qguard.json - # QMI system/etc/sysconfig/qti_whitelist.xml system_ext/etc/sysconfig/qti_telephony_system_packages_config.xml @@ -1715,6 +1731,7 @@ system_ext/etc/permissions/qcrilhook.xml system_ext/etc/permissions/qti_permissions.xml system_ext/etc/permissions/telephony_system-ext_privapp-permissions-qti.xml system_ext/etc/permissions/vendor.qti.ims.rcsservice.xml +system_ext/framework/mirilhook.jar system_ext/framework/qcrilhook.jar system_ext/framework/vendor.qti.ims.connection-V1.0-java.jar system_ext/framework/vendor.qti.ims.connectionaidlservice-V1-java.jar @@ -2063,6 +2080,7 @@ vendor/bin/thermal-engine-v2 vendor/etc/init/hw/init.mi_thermald.rc vendor/etc/init/init_thermal-engine-v2.rc vendor/etc/thermal-4k.conf +vendor/etc/thermal-boost.conf vendor/etc/thermal-camera.conf vendor/etc/thermal-cclassvideo.conf vendor/etc/thermal-cgame.conf diff --git a/props/system.prop b/props/system.prop index 6f4b4cec..47ffea50 100644 --- a/props/system.prop +++ b/props/system.prop @@ -1,3 +1,12 @@ +# Battery Saving +persist.radio.add_power_save=1 +pm.sleep_mode=1 +ro.ril.disable.power.collapse=0 +ro.ril.fast.dormancy.rule=1 +ro.ril.fast.dormancy.timeout=3 +ro.mot.eri.losalert.delay=100 +ro.vold.umsdirtyratio=20 + # CNE persist.vendor.cne.feature=1 diff --git a/props/vendor.prop b/props/vendor.prop index 62348bdb..fb1aa226 100644 --- a/props/vendor.prop +++ b/props/vendor.prop @@ -42,7 +42,7 @@ vendor.audio.feature.dmabuf.cma.memory.enable=true vendor.audio.feature.hfp.enable=false vendor.audio.feature.kpi_optimize.enable=true vendor.audio.hdr.record.enable=false -vendor.audio.offload.buffer.size.kb=32 +vendor.audio.offload.buffer.size.kb=256 vendor.audio.safx.pbe.enabled=false vendor.audio.ull_record_period_multiplier=2 vendor.audio.ultrasound.stoplatency=60 @@ -86,6 +86,13 @@ ro.hardware.camera=xiaomi # Chipset ro.soc.manufacturer=QTI +# Dex2oat +dalvik.vm.dex2oat64.enabled=true +dalvik.vm.dex2oat-cpu-set=4,5,6,7 +dalvik.vm.dex2oat-threads=4 +dalvik.vm.image-dex2oat-cpu-set=4,5,6,7 +dalvik.vm.image-dex2oat-threads=4 + # Display ro.vendor.display.sensortype=2 ro.vendor.display.svi=1 @@ -117,9 +124,6 @@ persist.vendor.dpm.vndr.idletimer.mode=default persist.vendor.fingerprint.type=udfps_optical persist.vendor.fingerprint.sensor_location=610|2437|105 -# FRP -ro.frp.pst=/dev/block/bootdevice/by-name/frp - # FUSE persist.sys.fuse.passthrough.enable=true @@ -129,37 +133,50 @@ vendor.gatekeeper.disable_spu=true # Graphics debug.sf.auto_latch_unsignaled=0 debug.sf.disable_client_composition_cache=1 +debug.sf.enable_transaction_tracing=false +debug.sf.enable_gl_backpressure=0 +debug.sf.hw=0 +debug.sf.predict_hwc_composition_strategy=0 +debug.sf.set_idle_timer_ms=0 +debug.sf.treat_170m_as_sRGB=1 debug.sf.early.app.duration=16500000 debug.sf.early.sf.duration=16000000 debug.sf.earlyGl.app.duration=21000000 debug.sf.earlyGl.sf.duration=13500000 -debug.sf.enable_gl_backpressure=1 +debug.sf.enable_gl_backpressure=0 debug.sf.enable_hwc_vds=1 debug.sf.hw=0 +debug.sf.enable_adpf_cpu_hint=true debug.sf.late.app.duration=20500000 debug.sf.late.sf.duration=10500000 -debug.sf.predict_hwc_composition_strategy=0 -debug.sf.set_idle_timer_ms=1100 -debug.sf.treat_170m_as_sRGB=1 debug.sf.use_phase_offsets_as_durations=1 +debug.sf.defer_refresh_rate_when_off=1 +debug.sf.enable_egl_image_tracker=0 +persist.mm.enable.prefetch=true persist.sys.sf.color_mode=0 persist.sys.sf.color_saturation=1.0 +ro.gfx.driver.1=com.qualcomm.qti.gpudrivers.pineapple.api34 +persist.sys.force_vulkan=1 +persist.sys.purgeable_assets=1 +ro.surface_flinger.set_display_power_timer_ms=1000 +ro.surface_flinger.set_idle_timer_ms=4000 persist.sys.sf.native_mode=258 ro.hardware.egl=adreno ro.hardware.vulkan=adreno ro.opengles.version=196610 ro.surface_flinger.enable_frame_rate_override=false ro.surface_flinger.force_hwc_copy_for_virtual_displays=true +ro.surface_flinger.game_default_frame_rate_override=120 ro.surface_flinger.has_HDR_display=true ro.surface_flinger.has_wide_color_display=true -ro.surface_flinger.max_frame_buffer_acquired_buffers=3 ro.surface_flinger.max_virtual_display_dimension=4096 ro.surface_flinger.protected_contents=true ro.surface_flinger.set_touch_timer_ms=200 -ro.surface_flinger.supports_background_blur=1 +ro.surface_flinger.supports_background_blur=0 ro.surface_flinger.use_color_management=true ro.surface_flinger.use_content_detection_for_refresh_rate=true ro.surface_flinger.wcg_composition_dataspace=142671872 +ro.surface_flinger.clear_slots_with_set_layer_buffer=true vendor.gralloc.disable_ubwc=0 # Keystore @@ -170,8 +187,22 @@ debug.c2.use_dmabufheaps=1 debug.stagefright.c2inputsurface=-1 ro.media.xml_variant.profiles=_parrot_v0 +# Media (Dolby) +ro.vendor.audio.dolby.dax.support=true +ro.vendor.audio.dolby.surround.enable=true +vendor.audio.dolby.ds2.enabled=false +vendor.audio.dolby.ds2.hardbypass=false +debug.sdm.support_writeback=0 + +# Netflix +ro.netflix.bsp_rev=Q6450-36256-1 + # Perf ro.vendor.extension_library=libqti-perfd-client.so +vendor.perf.framepacing.enable=1 + +# Ram +persist.sys.purgeable_assets=1 # Qcomd persist.vendor.qcomsysd.enabled=1 @@ -216,3 +247,41 @@ persist.vendor.vcb.enable=false # WLAN wifi.aware.interface=wifi-aware0 + +# Netflix +vendor.netflix.bsp_rev=Q6450-36256-1 + +# Fling +ro.min.fling_velocity=160 +ro.max.fling_velocity=20000 +ro.vendor.qti.cgroup_follow.enable=true +persist.vendor.qti.inputopts.enable=true +persist.vendor.qti.inputopts.movetouchslop=0.6 +ro.qcom.adreno.qgl.ShaderStorageImageExtendedFormats=0 + +# HWUI +ro.hwui.render_ahead=15 +ro.hwui.drop_shadow_cache_size=6 +ro.hwui.gradient_cache_size=1 +ro.hwui.layer_cache_size=48 +ro.hwui.path_cache_size=32 +ro.hwui.r_buffer_cache_size=8 +ro.hwui.text_large_cache_height=1024 +ro.hwui.text_large_cache_width=2048 +ro.hwui.text_small_cache_height=1024 +ro.hwui.text_small_cache_width=1024 +ro.hwui.texture_cache_flushrate=0.4 +ro.hwui.texture_cache_size=72 +debug.hwui.use_hint_manager=true +debug.hwui.target_cpu_time_percent=30 +debug.hwui.renderer=skiavk +debug.hwui.show_overdraw=false +debug.hwui.use_triple_buffering=true +debug.hwui.vulkan.use_pipeline_cache=true +debug.renderengine.vulkan=true +debug.renderengine.backend=vulkan +renderthread.skia.reduceopstasksplitting=true +debug.gralloc.enable_fb_ubwc=1 + +# Performance +debug.performance.tuning=1 diff --git a/rootdir/etc/init.garnet.rc b/rootdir/etc/init.garnet.rc index 3a9176e4..9d717c7f 100644 --- a/rootdir/etc/init.garnet.rc +++ b/rootdir/etc/init.garnet.rc @@ -31,6 +31,11 @@ on boot chmod 0700 /sys/bus/platform/devices/soc:fingerprint_fpc/screen chmod 0666 /dev/input/event2 +on post-fs + # Workarround for cache + rm /data/resource-cache + rm /data/system/package_cache + on post-fs-data mkdir /data/vendor/misc 01771 system system mkdir /data/vendor/misc/mi_fp 0770 system system diff --git a/rootdir/etc/init.qcom.rc b/rootdir/etc/init.qcom.rc index a838a566..53e0d00d 100644 --- a/rootdir/etc/init.qcom.rc +++ b/rootdir/etc/init.qcom.rc @@ -540,6 +540,11 @@ on property:vold.decrypt=trigger_restart_framework start qcom-c_main-sh start wcnss-service +on post-fs-data + # Charge Bypass + chown system system /sys/class/qcom-battery/input_suspend + chmod 0660 /sys/class/qcom-battery/input_suspend + service vendor.qrtr-ns /vendor/bin/qrtr-ns -f class core user vendor_qrtr diff --git a/sepolicy/private/parts_app.te b/sepolicy/private/parts_app.te new file mode 100644 index 00000000..299f3521 --- /dev/null +++ b/sepolicy/private/parts_app.te @@ -0,0 +1,36 @@ +type parts_app, domain; +typeattribute parts_app mlstrustedsubject; + +app_domain(parts_app) + +binder_use(parts_app) + +allow parts_app { + activity_service + activity_task_service + audio_service + audioserver_service + autofill_service + batteryproperties_service + batterystats_service + content_capture_service + game_service + gpu_service + hint_service + media_session_service + netstats_service + radio_service + trust_service + sensorservice_service + surfaceflinger_service + vendor_perf_service + vibrator_manager_service + voiceinteraction_service +}:service_manager find; + +allow parts_app system_app_data_file:dir create_dir_perms; +allow parts_app system_app_data_file:{ + file + lnk_file +} create_file_perms; + diff --git a/sepolicy/private/property_contexts b/sepolicy/private/property_contexts new file mode 100644 index 00000000..e71213ae --- /dev/null +++ b/sepolicy/private/property_contexts @@ -0,0 +1,12 @@ +ro.camera.req.fmq. u:object_r:exported_default_prop:s0 +ro.camera.res.fmq. u:object_r:exported_default_prop:s0 +camera.test.auto u:object_r:exported_default_prop:s0 +ro.vendor.aware_available u:object_r:exported_default_prop:s0 +ro.hardware.chipname u:object_r:exported_default_prop:s0 + +ro.miui.region u:object_r:exported_system_prop:s0 +ro.miui.build.region u:object_r:exported_system_prop:s0 +ro.miui.cust_device u:object_r:exported_system_prop:s0 + +persist.camera.aiasd.isovalue u:object_r:vendor_persist_camera_prop:s0 +persist.vendor.low.cutoff u:object_r:vendor_persist_camera_prop:s0 diff --git a/sepolicy/private/seapp_contexts b/sepolicy/private/seapp_contexts new file mode 100644 index 00000000..097472e9 --- /dev/null +++ b/sepolicy/private/seapp_contexts @@ -0,0 +1,2 @@ +# EuiccGarnet +user=system seinfo=platform isPrivApp=true name=com.xiaomi.settings domain=parts_app type=system_app_data_file levelFrom=all diff --git a/sepolicy/vendor/attributes b/sepolicy/vendor/attributes new file mode 100644 index 00000000..3bbbd70a --- /dev/null +++ b/sepolicy/vendor/attributes @@ -0,0 +1,9 @@ +# Mi Cam +attribute vendor_hal_camerapostproc_xiaomi; +attribute vendor_hal_camerapostproc_xiaomi_client; +attribute vendor_hal_camerapostproc_xiaomi_server; + +# Dolby +attribute hal_dms; +attribute hal_dms_client; +attribute hal_dms_server; diff --git a/sepolicy/vendor/file.te b/sepolicy/vendor/file.te index 2291b21b..6f45698a 100644 --- a/sepolicy/vendor/file.te +++ b/sepolicy/vendor/file.te @@ -18,5 +18,8 @@ type sys_thermal_max_brightness, fs_type, sysfs_type; type sys_thermal_wifi_limit, fs_type, sysfs_type; type thermal_data_file, data_file_type, file_type; +# Walt +type proc_walt, fs_type, proc_type; + # Wifi type vendor_mac_vendor_data_file, data_file_type, file_type, mlstrustedobject; diff --git a/sepolicy/vendor/file_contexts b/sepolicy/vendor/file_contexts index 787ce638..b810995b 100644 --- a/sepolicy/vendor/file_contexts +++ b/sepolicy/vendor/file_contexts @@ -23,6 +23,11 @@ /vendor/lib(64)?/libcalculator\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libcalculator_htp\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libhta_hexagon_runtime_snpe\.so u:object_r:same_process_hal_file:s0 +/vendor/lib(64)?/libprocessgroup_shim\.so u:object_r:same_process_hal_file:s0 +/vendor/lib(64)?/libgui_shim\.so u:object_r:same_process_hal_file:s0 +/vendor/lib(64)?/libmialgoengine\.so u:object_r:same_process_hal_file:s0 +/vendor/lib(64)?/libmialgo_.*\.so u:object_r:same_process_hal_file:s0 +/vendor/lib(64)?/libcamera_algoup_jni\.xiaomi\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libmialgo_ai_vision\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libmialgo_utils\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libomp\.so u:object_r:same_process_hal_file:s0 @@ -41,6 +46,11 @@ /dev/mi_display/disp_feature u:object_r:vendor_displayfeature_device:s0 /sys/devices/virtual/mi_display/disp_feature/disp-DSI-+[0-1](/.*)? u:object_r:vendor_sysfs_displayfeature:s0 +# Dolby +/(vendor|system/vendor)/bin/hw/vendor\.dolby\.hardware\.dms@2\.0-service u:object_r:hal_dms_default_exec:s0 +/(vendor|system/vendor)/bin/hw/vendor\.dolby\.media\.c2@1\.0-service u:object_r:mediacodec_exec:s0 +/data/vendor/dolby(/.*)? u:object_r:vendor_data_file:s0 + # Fingerprint /vendor/bin/hw/android\.hardware\.biometrics\.fingerprint-service\.xiaomi u:object_r:hal_fingerprint_default_exec:s0 /data/vendor/fpc(/.*)? u:object_r:vendor_fingerprint_data_file:s0 diff --git a/sepolicy/vendor/genfs_contexts b/sepolicy/vendor/genfs_contexts index b1ab1cf1..3f906493 100644 --- a/sepolicy/vendor/genfs_contexts +++ b/sepolicy/vendor/genfs_contexts @@ -7,6 +7,16 @@ genfscon sysfs /devices/platform/soc/soc:spf_core_platform/soc:spf_core_platform # Fingerprint genfscon sysfs /devices/platform/soc/soc:fingerprint_fpc u:object_r:vendor_sysfs_fingerprint:s0 genfscon sysfs /devices/virtual/touch/touch_dev/fod_press_status u:object_r:sysfs_tp_fodstatus:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup26 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup9 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup8 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup11 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup15 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup16 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup19 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup22 u:object_r:sysfs_wakeup:s0 +genfscon sysfs /devices/platform/soc/soc:fpc1020/wakeup/wakeup23 u:object_r:sysfs_wakeup:s0 # Sensors genfscon sysfs /devices/virtual/touch/touch_dev/fod_longpress_gesture_enabled u:object_r:vendor_sysfs_sensors:s0 @@ -25,3 +35,6 @@ genfscon sysfs /devices/platform/soc/a94000.qcom,qup_uart/wakeup u:object_r:sysf genfscon sysfs /devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8350@0:pon_hlos@1300/c42d000.qcom,spmi:qcom,pmk8350@0:pon_hlos@1300:pwrkey-bark/wakeup u:object_r:sysfs_wakeup:s0 genfscon sysfs /devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8350@0:pon_hlos@1300/c42d000.qcom,spmi:qcom,pmk8350@0:pon_hlos@1300:pwrkey-resin-bark/wakeup u:object_r:sysfs_wakeup:s0 genfscon sysfs /devices/platform/soc/soc:fingerprint_goodix/wakeup u:object_r:sysfs_wakeup:s0 + +# Walt +genfscon proc /sys/walt u:object_r:proc_walt:s0 diff --git a/sepolicy/vendor/hal_audio.te b/sepolicy/vendor/hal_audio.te index a1a5b499..78bd03d9 100644 --- a/sepolicy/vendor/hal_audio.te +++ b/sepolicy/vendor/hal_audio.te @@ -3,4 +3,6 @@ allow hal_audio_default hal_sensors_default:unix_stream_socket connectto; allow hal_audio_default smartpa_type:file r_file_perms; allow hal_audio_default vendor_persist_audio_file:file rw_file_perms; +hal_client_domain(hal_audio_default, hal_dms) + set_prop(hal_audio_default, vendor_audio_prop) diff --git a/sepolicy/vendor/hal_camera.te b/sepolicy/vendor/hal_camera.te index 851d6b00..2f5e9ab8 100644 --- a/sepolicy/vendor/hal_camera.te +++ b/sepolicy/vendor/hal_camera.te @@ -1,6 +1,3 @@ -attribute vendor_hal_camerapostproc_xiaomi; -attribute vendor_hal_camerapostproc_xiaomi_client; -attribute vendor_hal_camerapostproc_xiaomi_server; type hal_quickcamera_hwservice, hwservice_manager_type; type vendor_hal_camerapostproc_xiaomi_hwservice, hwservice_manager_type; @@ -28,8 +25,10 @@ allow vendor_hal_camerapostproc_xiaomi_server vendor_hal_camerapostproc_xiaomi_c allow vendor_hal_camerapostproc_xiaomi_server vendor_hal_camerapostproc_xiaomi_client:binder { call transfer }; allow vendor_hal_camerapostproc_xiaomi_server vendor_hal_camerapostproc_xiaomi_client:fd *; get_prop(hal_camera_default, serialno_prop) +hal_client_domain(hal_camera_default, hal_power) hal_server_domain(hal_camera_default, vendor_hal_camerapostproc_xiaomi) r_dir_file(hal_camera_default, mnt_vendor_file) r_dir_file(hal_camera_default, vendor_persist_sensors_file) set_prop(hal_camera_default, vendor_camera_p3enable_prop) set_prop(hal_camera_default, vendor_camera_sensor_prop) +set_prop(hal_camera_default, vendor_camera_prop) diff --git a/sepolicy/vendor/hal_camera_default.te b/sepolicy/vendor/hal_camera_default.te new file mode 100644 index 00000000..6924104b --- /dev/null +++ b/sepolicy/vendor/hal_camera_default.te @@ -0,0 +1,17 @@ +binder_call(hal_camera_default, platform_app) + +hal_client_domain(hal_camera_default, vendor_hal_qspnhal) + +allow hal_camera_default proc_pressure_cpu:file r_file_perms; +allow hal_camera_default proc_pressure_mem:file r_file_perms; +allow hal_camera_default proc_meminfo:file r_file_perms; +allow hal_camera_default proc_pressure_io:file r_file_perms; + +allow hal_camera_default vendor_sysfs_kgsl:file r_file_perms; +allow hal_camera_default vendor_sysfs_kgsl:dir r_dir_perms; + +allow hal_camera_default sysfs_devices_system_cpu:file r_file_perms; + +allow hal_camera_default sysfs:file { read open getattr }; +allow hal_camera_default proc_meminfo:file r_file_perms; +allow hal_camera_default proc_pressure_cpu:file r_file_perms; diff --git a/sepolicy/vendor/hal_dms.te b/sepolicy/vendor/hal_dms.te new file mode 100644 index 00000000..e610c5cb --- /dev/null +++ b/sepolicy/vendor/hal_dms.te @@ -0,0 +1,24 @@ +type hal_dms_default, domain; +type hal_dms_default_exec, exec_type, file_type, vendor_file_type; +type hal_dms_hwservice, hwservice_manager_type; + +init_daemon_domain(hal_dms_default) + +hal_attribute_hwservice(hal_dms, hal_dms_hwservice) +hal_server_domain(hal_dms_default, hal_dms) + +add_hwservice(hal_dms_server, hal_dms_hwservice) + +binder_call(hal_dms_client, hal_dms_server) +binder_call(hal_dms_server, hal_dms_client) + +allow hal_dms_default hal_dms_hwservice:hwservice_manager add; + +allow hal_dms_default vendor_data_file:dir rw_dir_perms; +allow hal_dms_default vendor_data_file:file create_file_perms; +allow hal_dms_default property_socket:sock_file write; + +allow hal_dms_default vendor_audio_prop:property_service set; +allow hal_dms_default vendor_audio_prop:file {read getattr map open}; + +allow hal_dms_default init:unix_stream_socket connectto; diff --git a/sepolicy/vendor/hal_power.te b/sepolicy/vendor/hal_power.te index c514d1f9..92f56ab2 100644 --- a/sepolicy/vendor/hal_power.te +++ b/sepolicy/vendor/hal_power.te @@ -1,2 +1 @@ allow hal_power_default touchfeature_device:chr_file rw_file_perms; -rw_dir_file(hal_power_default, proc) diff --git a/sepolicy/vendor/hal_power_default.te b/sepolicy/vendor/hal_power_default.te new file mode 100644 index 00000000..d18b7e09 --- /dev/null +++ b/sepolicy/vendor/hal_power_default.te @@ -0,0 +1,3 @@ +allow hal_power_default proc_walt:dir search; +allow hal_power_default proc_walt:file rw_file_perms; +allow hal_power_default vendor_sysfs_msm_perf:file rw_file_perms; diff --git a/sepolicy/vendor/hwservice_contexts b/sepolicy/vendor/hwservice_contexts index fde869ee..2a150ddb 100644 --- a/sepolicy/vendor/hwservice_contexts +++ b/sepolicy/vendor/hwservice_contexts @@ -3,6 +3,9 @@ vendor.xiaomi.hardware.bgservice::IBGService u:object_r:vendor_h vendor.xiaomi.hardware.campostproc::IMiPostProcService u:object_r:vendor_hal_camerapostproc_xiaomi_hwservice:s0 vendor.xiaomi.hardware.quickcamera::IQuickCameraService u:object_r:hal_quickcamera_hwservice:s0 +# Dolby +vendor.dolby.hardware.dms::IDms u:object_r:hal_dms_hwservice:s0 + # Fingerprint vendor.xiaomi.hardware.fx.tunnel::IMiFxTunnel u:object_r:vendor_hal_fingerprint_hwservice_xiaomi:s0 diff --git a/sepolicy/vendor/platform_app.te b/sepolicy/vendor/platform_app.te new file mode 100644 index 00000000..8c5952df --- /dev/null +++ b/sepolicy/vendor/platform_app.te @@ -0,0 +1,25 @@ +hal_client_domain(platform_app, vendor_hal_camerapostproc_xiaomi) + +binder_use(platform_app) +binder_call(platform_app, hal_camera_default) +binder_call(platform_app, vendor_hal_camerapostproc_xiaomi) +allow platform_app vendor_hal_camerapostproc_xiaomi_hwservice:hwservice_manager find; + +allow platform_app adsprpcd_file:dir { open read search }; +allow platform_app vendor_xdsp_device:chr_file { ioctl open read write }; +allow platform_app vendor_qdsp_device:chr_file { ioctl open read write }; + +allow platform_app vendor_camera_data_file:dir { append map r_dir_perms write }; +allow platform_app vendor_camera_data_file:file { getattr open read }; + +allow platform_app same_process_hal_file:file { execute getattr open read map }; + +allow platform_app app_data_file:file execute; + +# Properties +get_prop(platform_app, vendor_camera_sensor_prop) +get_prop(platform_app, vendor_display_prop) +get_prop(platform_app, vendor_panel_info_prop) +get_prop(platform_app, vendor_persist_camera_prop) +get_prop(platform_app, vendor_audio_prop) +get_prop(platform_app, vendor_camera_p3enable_prop) diff --git a/sepolicy/vendor/qti_init_shell.te b/sepolicy/vendor/qti_init_shell.te index 008c4f89..8c331968 100644 --- a/sepolicy/vendor/qti_init_shell.te +++ b/sepolicy/vendor/qti_init_shell.te @@ -1 +1,3 @@ +allow vendor_qti_init_shell proc_walt:dir search; +allow vendor_qti_init_shell proc_walt:file rw_file_perms; allow vendor_qti_init_shell vendor_sysfs_qdss_dev:file { setattr write }; diff --git a/sepolicy/vendor/system_app.te b/sepolicy/vendor/system_app.te new file mode 100644 index 00000000..d47d940a --- /dev/null +++ b/sepolicy/vendor/system_app.te @@ -0,0 +1,22 @@ +# Allow system_app to access a get true info +allow system_app proc_stat:file read; +allow system_app sysfs_thermal:file write; +allow system_app vendor_sysfs_kgsl:file read; +allow system_app vendor_sysfs_kgsl_gpuclk:file read; +allow system_app proc_stat:file open; +allow system_app vendor_sysfs_kgsl:file open; +allow system_app vendor_sysfs_kgsl_gpuclk:file open; +allow system_app proc_stat:file { getattr open read }; +allow system_app vendor_sysfs_kgsl:file { getattr open read }; +allow system_app vendor_sysfs_kgsl_gpuclk:file { getattr open read }; + +# Fast Charging +allow system_app vendor_sysfs_battery_supply:dir { search }; +allow system_app vendor_sysfs_battery_supply:file { getattr open read }; +r_dir_file(system_app, vendor_sysfs_battery_supply) + +allow system_app sysfs_thermal:file { rw_file_perms getattr }; + +# Charge Bypass +allow system_app vendor_sysfs_battery_supply:dir search; +allow system_app vendor_sysfs_battery_supply:file { getattr open read write }; diff --git a/sepolicy/vendor/system_server.te b/sepolicy/vendor/system_server.te new file mode 100644 index 00000000..e36ef2dc --- /dev/null +++ b/sepolicy/vendor/system_server.te @@ -0,0 +1,3 @@ +# Allow system_server to read Fast Charging status +allow system_server vendor_sysfs_battery_supply:file { getattr open read }; +allow system_server vendor_sysfs_battery_supply:file rw_file_perms;