Settings: Implement RefreshRateManager [SQUASHED] [2/2]

Includes:
- Per-app refresh rate config
- Extreme refresh rate (Force all apps to run in maximum refresh rate)
- Temp refresh rate allowed (Used in MEMC)
- Refresh rate QS tile

Change-Id: I30024eb8ce63f00a6f2fa1ea0230b004ff93ac74
Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Create BasePerAppConfigFragment

* Allow more preference types instead of only ListPreference.

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Create PerAppSwitchConfigFragment

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Add top info for per-app settings

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Don't show system apps for sensor block fragment

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Pass uid in per-app config fragment

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Allow specific allowed system app list for per-app fragment

* We need to allow dialer/messages/contact for fullscreen display settings.

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Make sure per-app list has no duplicates

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Warn user app reload before switching fullscreen display

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Adapt for Android U QPR2

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

Settings: Use ArrayMap/Set instead of HashMap/Set

Signed-off-by: cjh1249131356 <cjh1249131356@gmail.com>

[joeyhuab: Squashed all changes by same author]

Settings: Add null check for PreferenceScreen in BasePerAppConfigFragment

Co-authored-by: NurKeinNeid <nurkeinneid@derpfest.org>
Signed-off-by: NurKeinNeid <nurkeinneid@derpfest.org>
This commit is contained in:
cjh1249131356
2024-01-06 22:36:19 +08:00
committed by Joey
parent 08c76e85f3
commit 2fdab6cf4d
21 changed files with 1142 additions and 3 deletions

View File

@@ -5802,6 +5802,17 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Screen refresh rate -->
<activity android:name="Settings$ScreenRefreshRateActivity"
android:label="@string/screen_refresh_rate_title"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="org.evolution.settings.display.refreshrate.ScreenRefreshRateFragment"/>
</activity>
<!-- App lock -->
<activity android:name=".security.applock.AppLockCredentialActivity"

View File

@@ -10,7 +10,7 @@
-keep public class com.android.settings*.** extends androidx.fragment.app.Fragment {
void <init>();
}
-keep class org.evolution.settings.**
-keep,allowoptimization,allowaccessmodification class org.evolution.settings.** { *; }
# Keep all preference controllers needed by slice and DashboardFragment.
-keep class * extends com.android.settings.core.BasePreferenceController {

View File

@@ -408,4 +408,25 @@
<!-- Pulse on music track change -->
<string name="pulse_on_new_tracks_title">Music ticker</string>
<string name="pulse_on_new_tracks_summary">Show Ambient Display when a new music track is played</string>
<!-- Per-app configs -->
<string name="per_app_config_empty_text">No installed apps support current settings</string>
<!-- Per-app refresh rate -->
<string name="per_app_refresh_rate_title">Per-app refresh rate config</string>
<string name="per_app_refresh_rate_summary">Configure refresh rate individually for each apps</string>
<string name="per_app_refresh_rate_default">Follow system</string>
<!-- Screen refresh rate -->
<string name="screen_refresh_rate_title">Screen refresh rate</string>
<string name="min_refresh_rate_title">Minimum refresh rate</string>
<string name="max_refresh_rate_title">Maximum refresh rate</string>
<!-- Extreme refresh rate -->
<string name="extreme_refresh_rate_title">Extreme refresh rate</string>
<string name="extreme_refresh_rate_summary">Force highest refresh rate for all apps</string>
<string name="extreme_refresh_rate_warning">This will ensure system select highest refresh rate, ignoring user selected and app preferred options. Apps that require specific refresh rate may run abnormally.</string>
<!-- Confirm dialog -->
<string name="confirm_before_enable_title">Continue to enable?</string>
</resources>

View File

@@ -77,4 +77,18 @@
<!-- Device specific doze package -->
<string name="config_customDozePackage" translatable="false"></string>
<!-- List of system apps that are allowed to configure per-app settings -->
<string-array name="config_perAppConfAllowedSystemApps" translatable="false">
<item>com.android.chrome</item>
<item>com.android.vending</item>
<item>com.oneplus.gallery</item>
<item>com.google.android.apps.docs</item>
<item>com.google.android.apps.nbu.files</item>
<item>com.google.android.apps.photos</item>
<item>com.google.android.apps.recorder</item>
<item>com.google.android.calculator</item>
<item>com.google.android.calendar</item>
<item>com.google.android.gm</item>
</string-array>
</resources>

View File

@@ -181,6 +181,12 @@
settings:keywords="@string/keywords_screen_resolution"
settings:controller="com.android.settings.display.ScreenResolutionController"/>
<Preference
android:key="screen_refresh_rate"
android:title="@string/screen_refresh_rate_title"
android:fragment="org.evolution.settings.display.refreshrate.ScreenRefreshRateFragment"
settings:controller="org.evolution.settings.display.refreshrate.ScreenRefreshRateController"/>
<SwitchPreferenceCompat
android:key="display_white_balance"
android:title="@string/display_white_balance_title"
@@ -203,7 +209,7 @@
settings:controller="com.android.settings.display.HdrDisplayBrightnessPreferenceController"
settings:interval="1"/>
<ListPreference
<!--<ListPreference
android:key="max_refresh_rate"
android:title="@string/max_refresh_rate_title"
android:summary="@string/summary_placeholder"
@@ -219,7 +225,7 @@
android:key="peak_refresh_rate"
android:title="@string/peak_refresh_rate_title"
android:summary="@string/peak_refresh_rate_summary"
settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController"/>
settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController"/>-->
<SwitchPreferenceCompat
android:key="show_operator_name"

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023-2024 Nameless-AOSP
SPDX-License-Identifier: Apache-2.0
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="per_app_refresh_rate_scren"
android:title="@string/per_app_refresh_rate_title"
settings:searchable="false" />

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023-2024 Nameless-AOSP
SPDX-License-Identifier: Apache-2.0
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/screen_refresh_rate_title">
<ListPreference
android:key="min_refresh_rate"
android:title="@string/min_refresh_rate_title"
android:summary="@string/summary_placeholder"
settings:controller="org.evolution.settings.display.refreshrate.MinRefreshRatePreferenceController" />
<ListPreference
android:key="peak_refresh_rate"
android:title="@string/max_refresh_rate_title"
android:summary="@string/summary_placeholder"
settings:controller="org.evolution.settings.display.refreshrate.PeakRefreshRatePreferenceController"/>
<Preference
android:key="per_app_refresh_rate"
android:title="@string/per_app_refresh_rate_title"
android:summary="@string/per_app_refresh_rate_summary"
android:fragment="org.evolution.settings.display.refreshrate.PerAppRefreshRateFragment"
settings:controller="org.evolution.settings.display.refreshrate.PerAppRefreshRatePreferenceController"/>
<SwitchPreferenceCompat
android:key="extreme_refresh_rate"
android:title="@string/extreme_refresh_rate_title"
android:summary="@string/extreme_refresh_rate_summary"
settings:controller="org.evolution.settings.display.refreshrate.ExtremeRefreshRatePreferenceController"/>
</PreferenceScreen>

View File

@@ -545,4 +545,6 @@ public class Settings extends SettingsActivity {
public static class SmartPixelsActivity extends SettingsActivity {}
public static class DevRunningServicesActivity extends SettingsActivity { /* empty */ }
public static class ScreenRefreshRateActivity extends SettingsActivity {}
}

View File

@@ -22,6 +22,7 @@ import com.android.settings.MainClear;
import com.android.settings.MainClearConfirm;
import com.android.settings.ResetNetwork;
import com.android.settings.Settings;
import com.android.settings.Settings.ScreenRefreshRateActivity;
import com.android.settings.TestingSettings;
import com.android.settings.TrustedCredentialsSettings;
import com.android.settings.accessibility.AccessibilityDetailsSettingsFragment;
@@ -221,6 +222,7 @@ import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSetting
import com.android.settings.wifi.tether.WifiTetherSettings;
import org.evolution.settings.EvolutionSettings;
import org.evolution.settings.display.refreshrate.ScreenRefreshRateFragment;
import org.evolution.settings.fragments.miscellaneous.SmartPixels;
public class SettingsGateway {
@@ -433,6 +435,7 @@ public class SettingsGateway {
VolumeStepsSettings.class.getName(),
SmartPixels.class.getName(),
RunningServices.class.getName(),
ScreenRefreshRateFragment.class.getName(),
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
@@ -484,5 +487,6 @@ public class SettingsGateway {
Settings.PrivateDnsSettingsActivity.class.getName(),
Settings.SmartPixelsActivity.class.getName(),
Settings.DevRunningServicesActivity.class.getName(),
Settings.ScreenRefreshRateActivity.class.getName(),
};
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023-2024 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.content.Context;
import android.content.DialogInterface;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import org.evolution.display.RefreshRateManager;
public class ExtremeRefreshRatePreferenceController extends TogglePreferenceController {
private final RefreshRateManager mRefreshRateManager;
public ExtremeRefreshRatePreferenceController(Context context, String key) {
super(context, key);
mRefreshRateManager = context.getSystemService(RefreshRateManager.class);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean isChecked() {
return Settings.System.getIntForUser(mContext.getContentResolver(),
EXTREME_REFRESH_RATE, 0, UserHandle.USER_CURRENT) == 1;
}
@Override
public boolean setChecked(boolean isChecked) {
if (!isChecked) {
mRefreshRateManager.setExtremeRefreshRateEnabled(false);
return true;
}
new AlertDialog.Builder(mContext)
.setTitle(mContext.getText(R.string.confirm_before_enable_title))
.setMessage(mContext.getText(R.string.extreme_refresh_rate_warning))
.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mRefreshRateManager.setExtremeRefreshRateEnabled(true);
}
})
.setNegativeButton(R.string.cancel, null)
.show();
return isChecked();
}
@Override
public int getSliceHighlightMenuRes() {
return 0;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2020 The LineageOS Project
* Copyright (C) 2022-2024 Nameless-AOSP
*
* 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.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.util.evolution.DisplayRefreshRateHelper;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import java.util.ArrayList;
import java.util.List;
public class MinRefreshRatePreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private final ArrayList<Integer> mSupportedList;
private final DisplayRefreshRateHelper mHelper;
private ListPreference mListPreference;
public MinRefreshRatePreferenceController(Context context, String key) {
super(context, key);
mHelper = DisplayRefreshRateHelper.getInstance(context);
mSupportedList = mHelper.getSupportedRefreshRateList();
}
@Override
public int getAvailabilityStatus() {
return mSupportedList.size() > 1 ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mListPreference = screen.findPreference(getPreferenceKey());
final List<String> entries = new ArrayList<>();
final List<String> values = new ArrayList<>();
for (int i = 0; i < mSupportedList.size(); ++i) {
final String refreshRate = String.valueOf(mSupportedList.get(i));
entries.add(refreshRate + " Hz");
values.add(refreshRate);
}
mListPreference.setEntries(entries.toArray(new String[entries.size()]));
mListPreference.setEntryValues(values.toArray(new String[values.size()]));
}
@Override
public void updateState(Preference preference) {
final String refreshRate = String.valueOf(mHelper.getMinimumRefreshRate());
final int index = mListPreference.findIndexOfValue(refreshRate);
if (index != -1) {
mListPreference.setValueIndex(index);
mListPreference.setSummary(mListPreference.getEntries()[index]);
} else {
mListPreference.setSummary(refreshRate + " Hz");
}
final int minRefreshRate = Integer.parseInt(refreshRate);
final int peakRefreshRate = mHelper.getPeakRefreshRate();
if (peakRefreshRate < minRefreshRate) {
mHelper.setPeakRefreshRate(minRefreshRate);
}
final boolean extremeMode = Settings.System.getIntForUser(
mContext.getContentResolver(), EXTREME_REFRESH_RATE,
0, UserHandle.USER_CURRENT) != 0;
mListPreference.setEnabled(!extremeMode);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
mHelper.setMinimumRefreshRate(Integer.parseInt((String) newValue));
updateState(preference);
return true;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2020 The LineageOS Project
* Copyright (C) 2022-2024 Nameless-AOSP
*
* 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.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.util.evolution.DisplayRefreshRateHelper;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import java.util.ArrayList;
import java.util.List;
public class PeakRefreshRatePreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private final ArrayList<Integer> mSupportedList;
private final DisplayRefreshRateHelper mHelper;
private ListPreference mListPreference;
public PeakRefreshRatePreferenceController(Context context, String key) {
super(context, key);
mHelper = DisplayRefreshRateHelper.getInstance(context);
mSupportedList = mHelper.getSupportedRefreshRateList();
}
@Override
public int getAvailabilityStatus() {
return mSupportedList.size() > 1 ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mListPreference = screen.findPreference(getPreferenceKey());
final List<String> entries = new ArrayList<>();
final List<String> values = new ArrayList<>();
for (int i = 0; i < mSupportedList.size(); ++i) {
final String refreshRate = String.valueOf(mSupportedList.get(i));
entries.add(refreshRate + " Hz");
values.add(refreshRate);
}
mListPreference.setEntries(entries.toArray(new String[entries.size()]));
mListPreference.setEntryValues(values.toArray(new String[values.size()]));
}
@Override
public void updateState(Preference preference) {
final String refreshRate = String.valueOf(mHelper.getPeakRefreshRate());
final int index = mListPreference.findIndexOfValue(refreshRate);
if (index != -1) {
mListPreference.setValueIndex(index);
mListPreference.setSummary(mListPreference.getEntries()[index]);
} else {
mListPreference.setSummary(refreshRate + " Hz");
}
final int peakRefreshRate = Integer.parseInt(refreshRate);
final int minRefreshRate = mHelper.getMinimumRefreshRate();
if (minRefreshRate > peakRefreshRate) {
mHelper.setMinimumRefreshRate(peakRefreshRate);
}
final boolean extremeMode = Settings.System.getIntForUser(
mContext.getContentResolver(), EXTREME_REFRESH_RATE,
0, UserHandle.USER_CURRENT) != 0;
mListPreference.setEnabled(!extremeMode);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
mHelper.setPeakRefreshRate(Integer.parseInt(String.valueOf(newValue)));
updateState(preference);
return true;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2023-2024 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.display.refreshrate;
import android.os.Bundle;
import com.android.internal.util.evolution.DisplayRefreshRateHelper;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
import org.evolution.display.RefreshRateManager;
import org.evolution.settings.fragment.PerAppListConfigFragment;
public class PerAppRefreshRateFragment extends PerAppListConfigFragment {
private DisplayRefreshRateHelper mHelper;
private RefreshRateManager mRefreshRateManager;
private List<String> mEntries;
private List<Integer> mValues;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHelper = DisplayRefreshRateHelper.getInstance(getActivity());
mRefreshRateManager = getActivity().getSystemService(RefreshRateManager.class);
mEntries = new ArrayList<>();
mEntries.add(getString(R.string.per_app_refresh_rate_default));
mValues = new ArrayList<>();
mValues.add(-1);
for (int refreshRate : mHelper.getSupportedRefreshRateList()) {
mEntries.add(String.valueOf(refreshRate) + " Hz");
mValues.add(refreshRate);
}
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.per_app_refresh_rate_config;
}
@Override
protected int getTopInfoResId() {
return R.string.per_app_refresh_rate_summary;
}
@Override
protected List<String> getEntries() {
return mEntries;
}
@Override
protected List<Integer> getValues() {
return mValues;
}
@Override
protected int getCurrentValue(String packageName, int uid) {
return mRefreshRateManager.getRefreshRateForPackage(packageName);
}
@Override
protected void onValueChanged(String packageName, int uid, int value) {
if (value > 0) {
mRefreshRateManager.setRefreshRateForPackage(packageName, value);
} else {
mRefreshRateManager.unsetRefreshRateForPackage(packageName);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.util.evolution.DisplayRefreshRateHelper;
import com.android.settings.core.BasePreferenceController;
public class PerAppRefreshRatePreferenceController extends BasePreferenceController {
private final DisplayRefreshRateHelper mHelper;
private Preference mPreference;
public PerAppRefreshRatePreferenceController(Context context, String key) {
super(context, key);
mHelper = DisplayRefreshRateHelper.getInstance(context);
}
@Override
public int getAvailabilityStatus() {
return mHelper.getSupportedRefreshRateList().size() > 1
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public void updateState(Preference preference) {
final boolean extremeMode = Settings.System.getIntForUser(
mContext.getContentResolver(), EXTREME_REFRESH_RATE,
0, UserHandle.USER_CURRENT) != 0;
mPreference.setEnabled(!extremeMode);
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2023 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.MIN_REFRESH_RATE;
import static android.provider.Settings.System.PEAK_REFRESH_RATE;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.util.evolution.DisplayRefreshRateHelper;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
public class ScreenRefreshRateController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop {
private final DisplayRefreshRateHelper mHelper;
private Preference mPreference;
private SettingObserver mSettingObserver;
public ScreenRefreshRateController(Context context, String key) {
super(context, key);
mHelper = DisplayRefreshRateHelper.getInstance(context);
}
@Override
public int getAvailabilityStatus() {
return mHelper.getSupportedRefreshRateList().size() > 1
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
mSettingObserver = new SettingObserver(mPreference);
}
@Override
public void onStart() {
if (mSettingObserver != null) {
mSettingObserver.register(mContext.getContentResolver());
mSettingObserver.onChange(false);
}
}
@Override
public void onStop() {
if (mSettingObserver != null) {
mSettingObserver.unregister(mContext.getContentResolver());
}
}
@Override
public void updateState(Preference preference) {
String summary = null;
final int minRefreshRate = mHelper.getMinimumRefreshRate();
final int maxRefreshRate = mHelper.getPeakRefreshRate();
final boolean extremeMode = Settings.System.getIntForUser(
mContext.getContentResolver(), EXTREME_REFRESH_RATE,
0, UserHandle.USER_CURRENT) != 0;
final ArrayList<Integer> supportedList = mHelper.getSupportedRefreshRateList();
if (extremeMode && supportedList.size() > 0) {
final int maxAllowed = supportedList.get(supportedList.size() - 1);
summary = String.valueOf(maxAllowed) + " Hz";
} else if (minRefreshRate == maxRefreshRate) {
summary = String.valueOf(maxRefreshRate) + " Hz";
} else {
summary = String.valueOf(minRefreshRate) + " ~ " + String.valueOf(maxRefreshRate) + " Hz";
}
if (summary != null) {
preference.setSummary(summary);
}
super.updateState(preference);
}
private class SettingObserver extends ContentObserver {
private final Uri mMinUri = Settings.System.getUriFor(MIN_REFRESH_RATE);
private final Uri mPeakUri = Settings.System.getUriFor(PEAK_REFRESH_RATE);
private final Uri mExtremeUri = Settings.System.getUriFor(EXTREME_REFRESH_RATE);
private final Preference mPreference;
SettingObserver(Preference preference) {
super(new Handler());
mPreference = preference;
}
public void register(ContentResolver cr) {
cr.registerContentObserver(mMinUri, false, this);
cr.registerContentObserver(mPeakUri, false, this);
}
public void unregister(ContentResolver cr) {
cr.unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange) {
updateState(mPreference);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.display.refreshrate;
import static android.provider.Settings.System.MIN_REFRESH_RATE;
import static android.provider.Settings.System.PEAK_REFRESH_RATE;
import static android.provider.Settings.System.EXTREME_REFRESH_RATE;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
public class ScreenRefreshRateFragment extends DashboardFragment {
private static final String TAG = "ScreenRefreshRateFragment";
private final SettingsObserver mObserver = new SettingsObserver();
private final class SettingsObserver extends ContentObserver {
private final Uri mMinUri = Settings.System.getUriFor(MIN_REFRESH_RATE);
private final Uri mPeakUri = Settings.System.getUriFor(PEAK_REFRESH_RATE);
private final Uri mExtremeUri = Settings.System.getUriFor(EXTREME_REFRESH_RATE);
SettingsObserver() {
super(new Handler());
}
void register(ContentResolver cr) {
cr.registerContentObserver(mMinUri, false, this);
cr.registerContentObserver(mPeakUri, false, this);
cr.registerContentObserver(mExtremeUri, false, this);
}
void unregister(ContentResolver cr) {
cr.unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
updatePreferenceStates();
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mObserver.register(getContext().getContentResolver());
}
@Override
public void onDestroy() {
mObserver.unregister(getContext().getContentResolver());
super.onDestroy();
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.PAGE_UNKNOWN;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.screen_refresh_rate;
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2023-2024 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.fragment;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.widget.TopIntroPreference;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public abstract class BasePerAppConfigFragment extends EmptyTextSettings {
protected Context mContext;
private PackageManager mPackageManager;
/**
* Comparator by label, if null or empty then packageName.
*/
private static class AppComparator implements Comparator<AppData> {
private final Collator mCollator = Collator.getInstance();
@Override
public final int compare(AppData a, AppData b) {
String sa = a.label;
if (TextUtils.isEmpty(sa)) sa = a.packageName;
String sb = b.label;
if (TextUtils.isEmpty(sb)) sb = b.packageName;
return mCollator.compare(sa, sb);
}
}
protected final class AppData {
protected String label;
protected String packageName;
protected int uid;
protected AppData(String label, String packageName, int uid) {
this.label = label;
this.packageName = packageName;
this.uid = uid;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AppData) {
final AppData other = (AppData) obj;
return label.equals(other.label) &&
packageName.equals(other.packageName) &&
uid == other.uid;
}
return false;
}
@Override
public int hashCode() {
int result = label.hashCode();
result = 31 * result + packageName.hashCode();
result = 31 * result + uid;
return result;
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mContext = getActivity();
mPackageManager = mContext.getPackageManager();
}
@Override
public void onResume() {
super.onResume();
final Context prefContext = getPrefContext();
final PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
return;
}
// Clear the prefs
screen.removeAll();
// Add TopIntroPreference if resource id is valid
if (getTopInfoResId() > 0) {
try {
final String title = mContext.getResources().getString(getTopInfoResId());
if (!TextUtils.isEmpty(title)) {
final TopIntroPreference topInfoPref = new TopIntroPreference(prefContext);
topInfoPref.setTitle(title);
screen.addPreference(topInfoPref);
}
} catch (Exception e) {}
}
// Rebuild the list of prefs
final ArrayList<AppData> apps = collectApps();
for (final AppData appData : apps) {
screen.addPreference(createAppPreference(prefContext, appData));
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setEmptyText(R.string.per_app_config_empty_text);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.PAGE_UNKNOWN;
}
/**
* @return the sorted list of AppData of all applications for current user,
* with extra system applications defined in R.array.config_perAppConfAllowedSystemApps.
*/
private ArrayList<AppData> collectApps() {
final ArraySet<AppData> apps = new ArraySet<>();
final List<PackageInfo> installedPackages =
mPackageManager.getInstalledPackages(0);
for (PackageInfo pi : installedPackages) {
if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
apps.add(new AppData(
pi.applicationInfo.loadLabel(mPackageManager).toString(),
pi.packageName, pi.applicationInfo.uid));
}
if (getAllowedSystemAppListResId() > 0) {
try {
final String[] systemApps = mContext.getResources().getStringArray(
getAllowedSystemAppListResId());
for (String app : systemApps) {
try {
final PackageInfo pi = mPackageManager.getPackageInfo(app, 0);
apps.add(new AppData(
pi.applicationInfo.loadLabel(mPackageManager).toString(),
app, pi.applicationInfo.uid));
} catch (NameNotFoundException e) {
}
}
} catch (Exception e) {}
}
final ArrayList<AppData> appList = new ArrayList<>();
appList.addAll(apps);
Collections.sort(appList, new AppComparator());
return appList;
}
protected Drawable getIcon(String packageName) {
Drawable loadIcon = null;
if (packageName != null) {
try {
loadIcon = mPackageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
}
}
return loadIcon != null ? loadIcon : mPackageManager.getDefaultActivityIcon();
}
protected int getAllowedSystemAppListResId() {
return R.array.config_perAppConfAllowedSystemApps;
}
protected int getTopInfoResId() {
return 0;
}
protected abstract Preference createAppPreference(Context prefContext, AppData appData);
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2023-2024 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.fragment;
import android.content.Context;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import java.util.List;
import org.evolution.settings.widget.AppListPreference;
public abstract class PerAppListConfigFragment extends BasePerAppConfigFragment {
@Override
protected Preference createAppPreference(Context prefContext, AppData appData) {
final ListPreference pref = new AppListPreference(prefContext);
pref.setIcon(getIcon(appData.packageName));
pref.setTitle(appData.label);
pref.setDialogTitle(appData.label);
int size = getEntries().size();
CharSequence[] entries = new CharSequence[size];
CharSequence[] values = new CharSequence[size];
for (int i = 0; i < size; ++i) {
entries[i] = getEntries().get(i);
values[i] = String.valueOf(getValues().get(i));
}
pref.setEntries(entries);
pref.setEntryValues(values);
final String value = String.valueOf(getCurrentValue(appData.packageName, appData.uid));
CharSequence summary = "-1";
for (int i = 0; i < size; ++i) {
if (values[i].equals(value)) {
summary = entries[i];
break;
}
}
pref.setValue(value);
pref.setSummary(summary);
pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String newValueStr = String.valueOf(newValue);
onValueChanged(appData.packageName, appData.uid, Integer.parseInt(newValueStr));
CharSequence[] entries = pref.getEntries();
CharSequence[] values = pref.getEntryValues();
CharSequence summary = entries[0];
for (int i = 0; i < entries.length; ++i) {
if (values[i].equals(newValueStr)) {
summary = entries[i];
break;
}
}
pref.setSummary(summary);
return true;
}
});
return pref;
}
protected abstract List<String> getEntries();
protected abstract List<Integer> getValues();
protected abstract int getCurrentValue(String packageName, int uid);
protected abstract void onValueChanged(String packageName, int uid, int value);
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2023-2024 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.fragment;
import android.content.Context;
import android.util.ArrayMap;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.SwitchPreferenceCompat;
import java.util.ArrayList;
import java.util.List;
import org.evolution.settings.widget.AppSwitchPreference;
public abstract class PerAppSwitchConfigFragment extends BasePerAppConfigFragment {
private final ArrayMap<String, Boolean> mPkgCheckState = new ArrayMap<>();
@Override
protected Preference createAppPreference(Context prefContext, AppData appData) {
final SwitchPreferenceCompat pref = new AppSwitchPreference(prefContext);
pref.setIcon(getIcon(appData.packageName));
pref.setTitle(appData.label);
pref.setSummary(appData.packageName);
final boolean checked = isChecked(appData.packageName, appData.uid);
pref.setChecked(checked);
mPkgCheckState.put(appData.packageName, checked);
pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean ret = onSetChecked((SwitchPreferenceCompat) preference,
appData.packageName, appData.uid, (Boolean) newValue);
if (ret) {
mPkgCheckState.put(appData.packageName, (Boolean) newValue);
onCheckedListUpdated(generateCheckedPkgList());
}
return ret;
}
});
return pref;
}
private List<String> generateCheckedPkgList() {
final List<String> ret = new ArrayList<>();
for (String pkg : mPkgCheckState.keySet()) {
if (mPkgCheckState.get(pkg)) {
ret.add(pkg);
}
}
return ret;
}
protected abstract boolean isChecked(String packageName, int uid);
protected abstract boolean onSetChecked(SwitchPreferenceCompat pref, String packageName, int uid, boolean checked);
protected abstract void onCheckedListUpdated(List<String> pkgList);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.widget;
import android.content.Context;
import android.util.AttributeSet;
import androidx.preference.ListPreference;
/**
* The ListPreference for the pages need to show apps icon.
*/
public class AppListPreference extends ListPreference {
public AppListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppListPreference(Context context) {
super(context);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 The Nameless-AOSP Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.evolution.settings.widget;
import android.content.Context;
import android.util.AttributeSet;
import androidx.preference.SwitchPreferenceCompat;
/**
* The SwitchPreference for the pages need to show apps icon.
*/
public class AppSwitchPreference extends SwitchPreferenceCompat {
public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppSwitchPreference(Context context) {
super(context);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
public AppSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(com.android.settingslib.widget.preference.app.R.layout.preference_app);
}
}