a71: parts: Introduce Display Satutaion Service and TileUtils

* Adjust Saturation Value Handling for Device Color Mode Compatibility

This update refines the saturation adjustment mechanism. When the seek bar progress is set, the method executes
the command "service call SurfaceFlinger 1022 f [saturation]" via Runtime.getRuntime().exec() to apply the saturation changes.
However, it was observed that when the device's color mode is set to "saturated," a saturation value of 1.0 does not trigger any visual changes.
To address this, the code now sets a value of 1.001 for a seek bar value of 100, ensuring the desired effect is consistently applied.

* TileUtils Implementation: Implemented TileUtils to handle the addition of Quick Settings tiles, specifically for the saturation feature.
This is done with reference to the guidelines provided by Google
https://developer.android.com/develop/ui/views/quicksettings-tiles#prompt-user
this ensuring a seamless integration and user experience when creating new tiles.

Change-Id: I77607e993353318f5c75c25ff25342d022aab3a1
Signed-off-by: Pabloescobar-reborn <pabloescobarreborn77@gmail.com>
Co-Authored-By: kenway214 <kenway214@outlook.com>
Co-Authored-By: AnierinB <anierin@evolution-x.org>
Co-Authored-By: SKULSHADY <anushekprasal@gmail.com>
Co-Authored-By: jhenrique09 <jhenrique09.mcz@hotmail.com>
Signed-off-by: UnmoveD <unmoved2121@gmail.com>
This commit is contained in:
kenway214
2024-08-21 22:57:03 +05:30
committed by intelgigabyte
parent 2e5138aa42
commit d862a07d1c
31 changed files with 1435 additions and 1 deletions

View File

@@ -26,9 +26,9 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
@@ -73,6 +73,18 @@
</intent-filter>
</receiver>
<!-- Thermal Profiles tile service -->
<service
android:name=".thermal.ThermalTileService"
android:label="@string/thermal_tile_label"
android:icon="@drawable/ic_thermal_tile"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<!-- Per-app refresh rate activity -->
<activity
android:name=".refreshrate.RefreshActivity"
@@ -247,5 +259,33 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- Display Saturation activity -->
<activity
android:name=".saturation.SaturationActivity"
android:label="@string/saturation_title"
android:exported="true">
<intent-filter>
<action android:name="com.android.settings.action.IA_SETTINGS" />
</intent-filter>
<meta-data
android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.display" />
<meta-data
android:name="com.android.settings.summary"
android:resource="@string/saturation_summary" />
</activity>
<!-- Display Saturation tile service -->
<service
android:name=".saturation.SaturationTileService"
android:label="@string/saturation_title"
android:icon="@drawable/ic_saturation_tile"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018-2022 crDroid Android Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0" >
<path
android:fillColor="?android:attr/colorControlNormal"
android:pathData="M24 4C12.972 4 4 12.972 4 24s8.972 20 20 20s20 -8.972 20 -20S35.028 4 24 4zM32.5 25.5h-17c-0.829 0 -1.5 -0.671 -1.5 -1.5s0.671 -1.5 1.5 -1.5h17c0.829 0 1.5 0.671 1.5 1.5S33.329 25.5 32.5 25.5z" />
</vector>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018-2022 crDroid Android Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0" >
<path
android:fillColor="?android:attr/colorControlNormal"
android:pathData="M24 4C12.972 4 4 12.972 4 24s8.972 20 20 20s20 -8.972 20 -20S35.028 4 24 4zM32.5 25.5h-7v7c0 0.829 -0.672 1.5 -1.5 1.5s-1.5 -0.671 -1.5 -1.5v-7h-7c-0.828 0 -1.5 -0.671 -1.5 -1.5s0.672 -1.5 1.5 -1.5h7v-7c0 -0.829 0.672 -1.5 1.5 -1.5s1.5 0.671 1.5 1.5v7h7c0.828 0 1.5 0.671 1.5 1.5S33.328 25.5 32.5 25.5z" />
</vector>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 Havoc-OS
2022 DerpFest
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0" >
<path
android:fillColor="?android:attr/colorControlNormal"
android:pathData="M14.123456746339798,12.042328000068665 c0,-0.7430335164070133 -0.6079365134239197,-1.3509700298309326 -1.3509700298309326,-1.3509700298309326 s-1.3509700298309326,0.6079365134239197 -1.3509700298309326,1.3509700298309326 s0.6079365134239197,1.3509700298309326 1.3509700298309326,1.3509700298309326 s1.3509700298309326,-0.6079365134239197 1.3509700298309326,-1.3509700298309326 zM12.772486716508865,5.962962865829468 c-3.3571605241298674,0 -6.079365134239197,2.7222046101093293 -6.079365134239197,6.079365134239197 L4.66666653752327,12.042328000068665 l2.7019400596618652,2.7019400596618652 l2.7019400596618652,-2.7019400596618652 L8.044091612100601,12.042328000068665 c0,-2.6141270077228547 2.1142680966854095,-4.728395104408264 4.728395104408264,-4.728395104408264 s4.728395104408264,2.1142680966854095 4.728395104408264,4.728395104408264 s-2.1142680966854095,4.728395104408264 -4.728395104408264,4.728395104408264 c-1.0199823725223542,0 -1.965661393404007,-0.3309876573085789 -2.742469160556793,-0.8781305193901066 l-0.9591887211799618,0.9726984214782719 C10.097566057443618,17.648853623867033 11.380987585783004,18.12169313430786 12.772486716508865,18.12169313430786 c3.3571605241298674,0 6.079365134239197,-2.7222046101093293 6.079365134239197,-6.079365134239197 s-2.7222046101093293,-6.079365134239197 -6.079365134239197,-6.079365134239197 z" />
</vector>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M16.41,18.59L15,20L7,12L15,4L16.41,5.41L9.83,12"
android:fillColor="?android:attr/colorAccent"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M7.59,5.41L9,4L17,12L9,20L7.59,18.59L14.17,12"
android:fillColor="?android:attr/colorAccent"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="6dp"
android:height="6dp"
android:viewportWidth="6"
android:viewportHeight="6">
<path
android:pathData="M3,0C4.65686,0 6,1.34315 6,3C6,4.65686 4.65685,6 3,6C1.34315,6 0,4.65685 0,3C0,1.34315 1.34315,0 3,0Z"
android:fillColor="?android:attr/colorAccent"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="6dp"
android:height="6dp"
android:viewportWidth="6"
android:viewportHeight="6">
<path
android:pathData="M3,0C4.65686,0 6,1.34315 6,3C6,4.65686 4.65685,6 3,6C1.34315,6 0,4.65685 0,3C0,1.34315 1.34315,0 3,0Z"
android:fillColor="?android:attr/colorAccent"
android:fillAlpha="0.24"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,11 @@
<!-- drawable/ic_saturation_tile -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#000"
android:pathData="M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z" />
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:fillType="evenOdd"
android:pathData="M19.58 9.42l1.54-1.54C21.68 9.14 22 10.53 22 12c0 5.52-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2c1.46 0 2.85 0.32 4.11 0.89l-1.53 1.53C13.77 4.15 12.9 4 12 4c-4.41 0-8 3.59-8 8s3.59 8 8 8 8-3.59 8-8c0-0.9-0.15-1.77-0.42-2.58Zm-8.35 4.43c-0.25-0.1-0.46-0.25-0.65-0.43-0.18-0.19-0.33-0.4-0.43-0.65-0.1-0.24-0.15-0.5-0.15-0.75 0-0.27 0.05-0.52 0.15-0.76 0.1-0.24 0.25-0.46 0.43-0.65L19 5l-5.61 8.42c-0.19 0.18-0.4 0.33-0.65 0.43-0.24 0.1-0.5 0.15-0.76 0.15s-0.51-0.05-0.75-0.15Z"/>
</vector>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foregroundGravity="center_horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="320dp"
android:scaleType="centerCrop"
android:cropToPadding="true"
android:contentDescription="@null" />
</FrameLayout>
</LinearLayout>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="320dp"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="48dp">
<FrameLayout
android:id="@+id/arrow_previous"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:paddingLeft="24dp"
android:layout_gravity="center_vertical|left"
android:contentDescription="@string/image_preview_previous_page_content_description">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:src="@drawable/ic_image_preview_arrow_left"/>
</FrameLayout>
<LinearLayout
android:id="@+id/viewGroup"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:gravity="center"/>
<FrameLayout
android:id="@+id/arrow_next"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:paddingRight="24dp"
android:layout_gravity="center_vertical|right"
android:contentDescription="@string/image_preview_next_page_content_description">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:src="@drawable/ic_image_preview_arrow_right"/>
</FrameLayout>
</FrameLayout>
</LinearLayout>

View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2017-2022 crDroid Android Project
SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/activatedBackgroundIndicator"
android:clipToPadding="false">
<LinearLayout
android:id="@android:id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="-4dp"
android:minWidth="60dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="48dp"
android:maxHeight="48dp" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
android:ellipsize="end" />
<RelativeLayout
android:id="@+id/value_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/summary"
android:layout_alignStart="@android:id/title" >
<TextView
android:id="@+id/value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="1"
android:ellipsize="end" />
<ImageView
android:id="@+id/reset"
android:src="@drawable/ic_custom_seekbar_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_toEndOf="@id/value"
android:layout_centerVertical="true" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/seekbar_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/value_frame"
android:layout_alignStart="@android:id/title" >
<ImageView
android:id="@+id/minus"
android:src="@drawable/ic_custom_seekbar_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" />
<ImageView
android:id="@+id/plus"
android:src="@drawable/ic_custom_seekbar_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true" />
<LinearLayout
android:id="@+id/seekbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_toEndOf="@id/minus"
android:layout_toStartOf="@id/plus"
android:layout_centerVertical="true" />
</RelativeLayout>
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end|center_vertical"
android:paddingStart="16dp"
android:orientation="vertical" />
</LinearLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
SPDX-License-Identifier: Apache-2.0
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/add_tile"
android:title="@string/tile_add" />
</menu>

View File

@@ -1,7 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<declare-styleable name="MinMaxSeekBarPreference">
<attr name="minValue" format="integer" />
<attr name="maxValue" format="integer" />
</declare-styleable>
<!-- Base attributes available to CustomSeekBarPreference. -->
<declare-styleable name="CustomSeekBarPreference">
<attr name="defaultValueText" format="string" />
<attr name="interval" format="integer" />
<attr name="showSign" format="boolean" />
<attr name="units" format="string|reference" />
<attr name="continuousUpdates" format="boolean" />
</declare-styleable>
</resources>

View File

@@ -51,6 +51,13 @@
<string name="charging_tile_discharging">Discharging</string>
<string name="charging_tile_not_charging">Not Charging</string>
<string name="charging_tile_error">Error</string>
<!-- Thermal Tile Strings -->
<string name="thermal_tile_label">Thermal Mode</string>
<string name="thermal_mode_default">Default Mode</string>
<string name="thermal_mode_performance">Performance Mode</string>
<string name="thermal_mode_battery_saver">Battery Saver Mode</string>
<string name="thermal_mode_gaming">Gaming Mode</string>
<!-- Content descriptions for accessibility -->
<string name="charging_tile_desc_charging">Charging at %1$s watts, %2$s amps, %3$s volts</string>
@@ -94,4 +101,20 @@
<string name="charge_bypass_notification_text">Battery charging is currently bypassed</string>
<string name="charge_bypass_smart_enabled">Smart charging bypass enabled at %d%%</string>
<string name="charge_bypass_smart_disabled">Smart charging bypass disabled</string>
<!-- Saturation -->
<string name="saturation_title">Display Saturation</string>
<string name="saturation_summary">Control the saturation level of the display</string>
<string name="saturation_footer_summary">Changing the color mode in display settings will override the saturation level, requiring it to be reapplied.</string>
<!-- Image preview -->
<string name="image_preview_content_description">Preview</string>
<string name="image_preview_next_page_content_description">Next</string>
<string name="image_preview_previous_page_content_description">Previous</string>
<!-- Custom seekbar -->
<string name="custom_seekbar_value">Value: <xliff:g id="v">%s</xliff:g></string>
<string name="custom_seekbar_default_value">by default</string>
<string name="custom_seekbar_default_value_to_set">Default value: <xliff:g id="v">%s</xliff:g>\nLong tap to set</string>
<string name="custom_seekbar_default_value_is_set">Default value is set</string>
</resources>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018-2014 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<com.android.settingslib.widget.LayoutPreference
android:key="saturation_preview"
android:layout="@layout/image_preview_layout"
android:selectable="false" />
<org.lineageos.settings.CustomSeekBarPreference
android:key="saturation"
android:max="200"
android:min="0"
android:defaultValue="100"
settings:continuousUpdates="true" />
<com.android.settingslib.widget.FooterPreference
android:key="saturation_footer"
android:title="@string/saturation_footer_summary"
android:selectable="false" />
</PreferenceScreen>

View File

@@ -32,6 +32,7 @@ import android.util.Log;
import android.view.Display;
import org.lineageos.settings.refreshrate.RefreshUtils;
import org.lineageos.settings.thermal.ThermalTileService;
public class BootCompletedReceiver extends BroadcastReceiver {
private static final String TAG = "Parts";

View File

@@ -0,0 +1,24 @@
/*
* 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 {
// Saturation
public static final String KEY_SATURATION = "saturation";
public static final String KEY_SATURATION_PREVIEW = "saturation_preview";
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.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 saturation
new Handler(Looper.getMainLooper()).postDelayed(() -> {
Log.d(TAG, "Applying saved saturation setting...");
applySavedSaturation(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");
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.service.quicksettings.TileService;
import android.service.quicksettings.Tile;
import android.util.Log;
import org.lineageos.settings.utils.FileUtils;
import org.lineageos.settings.utils.TileUtils;
import org.lineageos.settings.R;
public class ThermalTileService extends TileService {
private static final String TAG = "ThermalTileService";
private static final String THERMAL_SCONFIG = "/sys/class/thermal/thermal_message/sconfig";
private String[] modes;
private int currentMode = 0; // Default to the first mode
@Override
public void onStartListening() {
super.onStartListening();
modes = new String[]{
getString(R.string.thermal_mode_default),
getString(R.string.thermal_mode_performance),
getString(R.string.thermal_mode_battery_saver),
getString(R.string.thermal_mode_gaming)
};
currentMode = getCurrentThermalMode();
// If sconfig value is -1 or invalid, set it to default mode
if (currentMode == -1) {
currentMode = 0; // Default mode
setThermalMode(0); // Write default mode value to sconfig
}
updateTile(); // Ensure the tile displays the correct mode when added to quick settings
}
@Override
public void onClick() {
toggleThermalMode();
}
private void toggleThermalMode() {
currentMode = (currentMode + 1) % modes.length; // Cycle through modes
setThermalMode(currentMode);
updateTile();
}
private int getCurrentThermalMode() {
String line = FileUtils.readOneLine(THERMAL_SCONFIG);
if (line != null) {
try {
int value = Integer.parseInt(line.trim());
switch (value) {
case 20: return 0; // Default
case 10: return 1; // Performance
case 3: return 2; // Battery Saver
case 9: return 3; // Gaming
default: return 0; // Default if unknown value
}
} catch (NumberFormatException e) {
Log.e(TAG, "Error parsing thermal mode value: ", e);
}
}
return -1; // Indicate invalid or unknown mode
}
private void setThermalMode(int mode) {
int thermalValue;
switch (mode) {
case 0: thermalValue = 20; break; // Default
case 1: thermalValue = 10; break; // Performance
case 2: thermalValue = 3; break; // Battery Saver
case 3: thermalValue = 9; break; // Gaming
default: thermalValue = 20; break; // Default
}
// Write the new thermal value to the sconfig file
boolean success = FileUtils.writeLine(THERMAL_SCONFIG, String.valueOf(thermalValue));
Log.d(TAG, "Thermal mode changed to " + modes[mode] + ": " + success);
}
private void updateTile() {
Tile tile = getQsTile();
if (tile != null) {
tile.setLabel("Thermal Profile"); // Set the main label
tile.setSubtitle(modes[currentMode]); // Set the current mode as the subtitle
tile.updateTile();
}
}
}