Snap for 12667701 from 7448b324fa to 25Q1-release

Change-Id: Ie36e5b2dd74e5a668bc8f1724d72667cb2a94f4d
This commit is contained in:
Android Build Coastguard Worker
2024-11-16 23:06:30 +00:00
11 changed files with 416 additions and 82 deletions

View File

@@ -35,6 +35,8 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.annotation.XmlRes; import androidx.annotation.XmlRes;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
@@ -54,6 +56,7 @@ import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.CustomDialogPreferenceCompat; import com.android.settingslib.CustomDialogPreferenceCompat;
import com.android.settingslib.CustomEditTextPreferenceCompat; import com.android.settingslib.CustomEditTextPreferenceCompat;
import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.preference.PreferenceScreenCreator;
import com.android.settingslib.search.Indexable; import com.android.settingslib.search.Indexable;
import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.widget.LayoutPreference;
@@ -176,6 +179,24 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
} }
} }
@Override
protected final int getPreferenceScreenResId(@NonNull Context context) {
return getPreferenceScreenResId();
}
/** Returns if catalyst is enabled on current screen. */
protected final boolean isCatalystEnabled() {
return getPreferenceScreenCreator() != null;
}
protected @Nullable PreferenceScreenCreator getPreferenceScreenCreator() {
if (!Flags.catalyst()) {
return null;
}
Context context = getContext();
return context != null ? getPreferenceScreenCreator(context) : null;
}
public View setPinnedHeaderView(int layoutResId) { public View setPinnedHeaderView(int layoutResId) {
final LayoutInflater inflater = getActivity().getLayoutInflater(); final LayoutInflater inflater = getActivity().getLayoutInflater();
final View pinnedHeader = final View pinnedHeader =

View File

@@ -25,7 +25,6 @@ import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState; import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState;
@@ -58,7 +57,8 @@ import java.util.Map;
*/ */
@SearchIndexable @SearchIndexable
public class ContactsStorageSettings extends DashboardFragment public class ContactsStorageSettings extends DashboardFragment
implements SelectorWithWidgetPreference.OnClickListener, OnPreferenceClickListener { implements SelectorWithWidgetPreference.OnClickListener, OnPreferenceClickListener,
AuthenticatorHelper.OnAccountsUpdateListener {
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.contacts_storage_settings); new BaseSearchIndexProvider(R.xml.contacts_storage_settings);
private static final String TAG = "ContactsStorageSettings"; private static final String TAG = "ContactsStorageSettings";
@@ -72,13 +72,15 @@ public class ContactsStorageSettings extends DashboardFragment
public void onAttach(@NonNull Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
mAuthenticatorHelper = new AuthenticatorHelper(context, mAuthenticatorHelper = new AuthenticatorHelper(context,
new UserHandle(UserHandle.myUserId()), null); new UserHandle(UserHandle.myUserId()), this);
String[] accountTypes = getEligibleAccountTypes(); mAuthenticatorHelper.listenToAccountUpdates();
for (String accountType : accountTypes) { preloadEligibleAccountIcon();
// Preload the drawable for the account type to avoid the latency when rendering the
// account preference.
mAuthenticatorHelper.preloadDrawableForType(context, accountType);
} }
@Override
public void onDetach() {
super.onDetach();
mAuthenticatorHelper.stopListeningToAccountUpdates();
} }
@UiThread @UiThread
@@ -126,6 +128,12 @@ public class ContactsStorageSettings extends DashboardFragment
return false; return false;
} }
@Override
public void onAccountsUpdate(UserHandle userHandle) {
preloadEligibleAccountIcon();
refreshUI();
}
@Override @Override
public void onCreatePreferences(@NonNull Bundle savedInstanceState, public void onCreatePreferences(@NonNull Bundle savedInstanceState,
@NonNull String rootKey) { @NonNull String rootKey) {
@@ -139,6 +147,7 @@ public class ContactsStorageSettings extends DashboardFragment
// when creating eligible account preferences. // when creating eligible account preferences.
mAccountMap.clear(); mAccountMap.clear();
final PreferenceGroup preferenceGroup = findPreference(PREF_KEY_ACCOUNT_CATEGORY); final PreferenceGroup preferenceGroup = findPreference(PREF_KEY_ACCOUNT_CATEGORY);
preferenceGroup.removeAll();
// If the default account is SIM, we should show in the page, otherwise don't show. // If the default account is SIM, we should show in the page, otherwise don't show.
SelectorWithWidgetPreference simAccountPreference = buildSimAccountPreference(); SelectorWithWidgetPreference simAccountPreference = buildSimAccountPreference();
if (simAccountPreference != null) { if (simAccountPreference != null) {
@@ -152,12 +161,21 @@ public class ContactsStorageSettings extends DashboardFragment
// If there's no eligible account types, the "Add Account" preference should // If there's no eligible account types, the "Add Account" preference should
// not be shown to the users. // not be shown to the users.
if (getEligibleAccountTypes().length > 0) { if (getEligibleAccountTypes().length > 0) {
getPreferenceScreen().addPreference(buildAddAccountPreference(accounts.isEmpty())); preferenceGroup.addPreference(buildAddAccountPreference(accounts.isEmpty()));
} }
setupDeviceOnlyPreference(); setupDeviceOnlyPreference();
setDefaultAccountPreference(preferenceGroup); setDefaultAccountPreference(preferenceGroup);
} }
private void preloadEligibleAccountIcon() {
String[] accountTypes = getEligibleAccountTypes();
for (String accountType : accountTypes) {
// Preload the drawable for the account type to avoid the latency when rendering the
// account preference.
mAuthenticatorHelper.preloadDrawableForType(getContext(), accountType);
}
}
private void setupDeviceOnlyPreference() { private void setupDeviceOnlyPreference() {
SelectorWithWidgetPreference preference = findPreference(PREF_KEY_DEVICE_ONLY); SelectorWithWidgetPreference preference = findPreference(PREF_KEY_DEVICE_ONLY);
if (preference != null) { if (preference != null) {

View File

@@ -147,7 +147,7 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
mMetricsFeatureProvider.logClickedPreference(preference, getMetricsCategory()); mMetricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
} }
private void updateActivityTitleWithScreenTitle(PreferenceScreen screen) { protected void updateActivityTitleWithScreenTitle(PreferenceScreen screen) {
if (screen != null) { if (screen != null) {
final CharSequence title = screen.getTitle(); final CharSequence title = screen.getTitle();
if (!TextUtils.isEmpty(title)) { if (!TextUtils.isEmpty(title)) {

View File

@@ -308,11 +308,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
super.onDestroy(); super.onDestroy();
} }
@Override
protected final int getPreferenceScreenResId(@NonNull Context context) {
return getPreferenceScreenResId();
}
@Override @Override
protected abstract int getPreferenceScreenResId(); protected abstract int getPreferenceScreenResId();
@@ -413,7 +408,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
removeControllersForHybridMode(); removeControllersForHybridMode();
} }
setPreferenceScreen(screen); setPreferenceScreen(screen);
requireActivity().setTitle(screen.getTitle()); updateActivityTitleWithScreenTitle(screen);
} else { } else {
addPreferencesFromResource(resId); addPreferencesFromResource(resId);
screen = getPreferenceScreen(); screen = getPreferenceScreen();
@@ -447,19 +442,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
} }
} }
/** Returns if catalyst is enabled on current screen. */
protected final boolean isCatalystEnabled() {
return getPreferenceScreenCreator() != null;
}
private @Nullable PreferenceScreenCreator getPreferenceScreenCreator() {
if (!Flags.catalyst()) {
return null;
}
Context context = getContext();
return context != null ? getPreferenceScreenCreator(context) : null;
}
/** /**
* Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)} * Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)}
* on all {@link AbstractPreferenceController}s. * on all {@link AbstractPreferenceController}s.

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener
import android.hardware.SensorPrivacyManager.Sensors.CAMERA
import android.os.PowerManager
import android.os.UserManager
import android.provider.Settings
import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settingslib.RestrictedSwitchPreference
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObservableDelegate
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.datastore.SettingsStore
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.TwoStatePreference
import com.android.settingslib.preference.PreferenceBindingPlaceholder
import com.android.settingslib.preference.SwitchPreferenceBinding
// LINT.IfChange
class AdaptiveSleepPreference :
TwoStatePreference,
SwitchPreferenceBinding,
PreferenceLifecycleProvider,
PreferenceBindingPlaceholder, // not needed once controller class is cleaned up
PreferenceAvailabilityProvider,
PreferenceRestrictionMixin {
private var broadcastReceiver: BroadcastReceiver? = null
private var sensorPrivacyChangedListener: OnSensorPrivacyChangedListener? = null
override val key: String
get() = KEY
override val title: Int
get() = R.string.adaptive_sleep_title
override val summary: Int
get() = R.string.adaptive_sleep_description
override fun isIndexable(context: Context) = false
override fun isEnabled(context: Context) =
super<PreferenceRestrictionMixin>.isEnabled(context) && context.canBeEnabled()
override val restrictionKeys: Array<String>
get() = arrayOf(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT)
override fun isAvailable(context: Context) = context.isAdaptiveSleepSupported()
override fun createWidget(context: Context) = RestrictedSwitchPreference(context)
override fun storage(context: Context): KeyValueStore = Storage(context)
override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
@Suppress("UNCHECKED_CAST")
private class Storage(
private val context: Context,
private val settingsStore: SettingsStore = SettingsSecureStore.get(context),
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
override fun contains(key: String) = settingsStore.contains(key)
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
(context.canBeEnabled() && settingsStore.getBoolean(key) == true) as T
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
settingsStore.setBoolean(key, value as Boolean?)
}
override fun onStart(context: PreferenceLifecycleContext) {
val receiver =
object : BroadcastReceiver() {
override fun onReceive(receiverContext: Context, intent: Intent) {
context.notifyPreferenceChange(this@AdaptiveSleepPreference)
}
}
context.registerReceiver(
receiver,
IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED),
)
broadcastReceiver = receiver
val listener = OnSensorPrivacyChangedListener { _, _ ->
context.notifyPreferenceChange(this)
}
SensorPrivacyManager.getInstance(context).addSensorPrivacyListener(CAMERA, listener)
sensorPrivacyChangedListener = listener
}
override fun onStop(context: PreferenceLifecycleContext) {
broadcastReceiver?.let { context.unregisterReceiver(it) }
sensorPrivacyChangedListener?.let {
SensorPrivacyManager.getInstance(context).removeSensorPrivacyListener(it)
}
}
companion object {
const val KEY = Settings.Secure.ADAPTIVE_SLEEP
@Suppress("DEPRECATION")
private fun Context.canBeEnabled() =
AdaptiveSleepPreferenceController.hasSufficientPermission(packageManager) &&
getSystemService(PowerManager::class.java)?.isPowerSaveMode != true &&
!SensorPrivacyManager.getInstance(this).isSensorPrivacyEnabled(CAMERA)
}
}
// LINT.ThenChange(AdaptiveSleepPreferenceController.java)

View File

@@ -20,19 +20,16 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.android.settings.display.UtilsKt.isAdaptiveSleepSupported;
import android.Manifest; import android.Manifest;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.service.attention.AttentionService;
import android.text.TextUtils;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@@ -45,9 +42,10 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
// LINT.IfChange
/** The controller for Screen attention switch preference. */ /** The controller for Screen attention switch preference. */
public class AdaptiveSleepPreferenceController { public class AdaptiveSleepPreferenceController {
public static final String PREFERENCE_KEY = "adaptive_sleep"; public static final String PREFERENCE_KEY = Settings.Secure.ADAPTIVE_SLEEP;
private static final int DEFAULT_VALUE = 0; private static final int DEFAULT_VALUE = 0;
private final SensorPrivacyManager mPrivacyManager; private final SensorPrivacyManager mPrivacyManager;
private final RestrictionUtils mRestrictionUtils; private final RestrictionUtils mRestrictionUtils;
@@ -144,28 +142,10 @@ public class AdaptiveSleepPreferenceController {
: UNSUPPORTED_ON_DEVICE; : UNSUPPORTED_ON_DEVICE;
} }
static boolean isAdaptiveSleepSupported(Context context) {
return context.getResources().getBoolean(
com.android.internal.R.bool.config_adaptive_sleep_available)
&& isAttentionServiceAvailable(context);
}
private static boolean isAttentionServiceAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
final String resolvePackage = packageManager.getAttentionServicePackageName();
if (TextUtils.isEmpty(resolvePackage)) {
return false;
}
final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
resolvePackage);
final ResolveInfo resolveInfo = packageManager.resolveService(intent,
PackageManager.MATCH_SYSTEM_ONLY);
return resolveInfo != null && resolveInfo.serviceInfo != null;
}
static boolean hasSufficientPermission(PackageManager packageManager) { static boolean hasSufficientPermission(PackageManager packageManager) {
final String attentionPackage = packageManager.getAttentionServicePackageName(); final String attentionPackage = packageManager.getAttentionServicePackageName();
return attentionPackage != null && packageManager.checkPermission( return attentionPackage != null && packageManager.checkPermission(
Manifest.permission.CAMERA, attentionPackage) == PackageManager.PERMISSION_GRANTED; Manifest.permission.CAMERA, attentionPackage) == PackageManager.PERMISSION_GRANTED;
} }
} }
// LINT.ThenChange(AdaptiveSleepPreference.kt)

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display
import android.content.Context
import com.android.settings.R
import com.android.settings.Settings.ScreenTimeoutActivity
import com.android.settings.flags.Flags
import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
// TODO(b/368359967): The entry point logic is not yet migrated
@ProvidePreferenceScreen
class ScreenTimeoutScreen : PreferenceScreenCreator {
override val key: String
get() = KEY
override val title: Int
get() = R.string.screen_timeout
override fun isFlagEnabled(context: Context) = Flags.catalystScreenTimeout()
override fun fragmentClass() = ScreenTimeoutSettings::class.java
override fun hasCompleteHierarchy() = false
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) { +AdaptiveSleepPreference() }
override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
makeLaunchIntent(context, ScreenTimeoutActivity::class.java, metadata?.key)
companion object {
const val KEY = "screen_timeout"
}
}

View File

@@ -20,6 +20,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.OTHER_OPT
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import static com.android.settings.display.UtilsKt.isAdaptiveSleepSupported;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@@ -34,7 +36,9 @@ import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
@@ -80,8 +84,10 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility(); mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
if (!isCatalystEnabled()) {
mAdaptiveSleepController.updatePreference(); mAdaptiveSleepController.updatePreference();
} }
}
}; };
private DevicePolicyManager mDevicePolicyManager; private DevicePolicyManager mDevicePolicyManager;
@@ -123,7 +129,6 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
mInitialEntries = getResources().getStringArray(R.array.screen_timeout_entries); mInitialEntries = getResources().getStringArray(R.array.screen_timeout_entries);
mInitialValues = getResources().getStringArray(R.array.screen_timeout_values); mInitialValues = getResources().getStringArray(R.array.screen_timeout_values);
mAdaptiveSleepController = new AdaptiveSleepPreferenceController(context);
mAdaptiveSleepPermissionController = mAdaptiveSleepPermissionController =
new AdaptiveSleepPermissionPreferenceController(context); new AdaptiveSleepPermissionPreferenceController(context);
mAdaptiveSleepCameraStatePreferenceController = mAdaptiveSleepCameraStatePreferenceController =
@@ -136,8 +141,12 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
mPrivacyPreference.setSelectable(false); mPrivacyPreference.setSelectable(false);
mPrivacyPreference.setLayoutResource( mPrivacyPreference.setLayoutResource(
com.android.settingslib.widget.preference.footer.R.layout.preference_footer); com.android.settingslib.widget.preference.footer.R.layout.preference_footer);
if (!isCatalystEnabled()) {
mPrivacyManager = SensorPrivacyManager.getInstance(context); mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyChangedListener = (sensor, enabled) -> mAdaptiveSleepController.updatePreference(); mAdaptiveSleepController = new AdaptiveSleepPreferenceController(context);
mPrivacyChangedListener =
(sensor, enabled) -> mAdaptiveSleepController.updatePreference();
}
mAdditionalTogglePreferenceController = FeatureFactory.getFeatureFactory() mAdditionalTogglePreferenceController = FeatureFactory.getFeatureFactory()
.getDisplayFeatureProvider().createAdditionalPreference(context); .getDisplayFeatureProvider().createAdditionalPreference(context);
} }
@@ -166,10 +175,12 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
mAdaptiveSleepPermissionController.updateVisibility(); mAdaptiveSleepPermissionController.updateVisibility();
mAdaptiveSleepCameraStatePreferenceController.updateVisibility(); mAdaptiveSleepCameraStatePreferenceController.updateVisibility();
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility(); mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
mAdaptiveSleepController.updatePreference();
mContext.registerReceiver( mContext.registerReceiver(
mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
if (!isCatalystEnabled()) {
mAdaptiveSleepController.updatePreference();
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener); mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
}
mIsUserAuthenticated = false; mIsUserAuthenticated = false;
FeatureFactory.getFeatureFactory().getDisplayFeatureProvider().updatePreference( FeatureFactory.getFeatureFactory().getDisplayFeatureProvider().updatePreference(
mAdditionalTogglePreferenceController); mAdditionalTogglePreferenceController);
@@ -179,13 +190,17 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
mContext.unregisterReceiver(mReceiver); mContext.unregisterReceiver(mReceiver);
if (!isCatalystEnabled()) {
mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener); mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
} }
}
@Override @Override
public void updateCandidates() { public void updateCandidates() {
final String defaultKey = getDefaultKey(); final String defaultKey = getDefaultKey();
final PreferenceScreen screen = getPreferenceScreen(); final PreferenceScreen screen = getPreferenceScreen();
// Adaptive sleep preference is added to the screen when catalyst is enabled
Preference adaptiveSleepPreference = screen.findPreference(AdaptiveSleepPreference.KEY);
screen.removeAll(); screen.removeAll();
final List<? extends CandidateInfo> candidateList = getCandidates(); final List<? extends CandidateInfo> candidateList = getCandidates();
@@ -222,10 +237,16 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
FeatureFactory.getFeatureFactory().getDisplayFeatureProvider() FeatureFactory.getFeatureFactory().getDisplayFeatureProvider()
.addToScreen(mAdditionalTogglePreferenceController, screen); .addToScreen(mAdditionalTogglePreferenceController, screen);
if (isScreenAttentionAvailable(getContext())) { if (isAdaptiveSleepSupported(getContext())) {
mAdaptiveSleepPermissionController.addToScreen(screen); mAdaptiveSleepPermissionController.addToScreen(screen);
mAdaptiveSleepCameraStatePreferenceController.addToScreen(screen); mAdaptiveSleepCameraStatePreferenceController.addToScreen(screen);
if (adaptiveSleepPreference != null) {
// reset order for appending
adaptiveSleepPreference.setOrder(Preference.DEFAULT_ORDER);
screen.addPreference(adaptiveSleepPreference);
} else {
mAdaptiveSleepController.addToScreen(screen); mAdaptiveSleepController.addToScreen(screen);
}
mAdaptiveSleepBatterySaverPreferenceController.addToScreen(screen); mAdaptiveSleepBatterySaverPreferenceController.addToScreen(screen);
screen.addPreference(mPrivacyPreference); screen.addPreference(mPrivacyPreference);
} }
@@ -307,6 +328,11 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
return R.xml.screen_timeout_settings; return R.xml.screen_timeout_settings;
} }
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return ScreenTimeoutScreen.KEY;
}
@Override @Override
public int getHelpResource() { public int getHelpResource() {
return R.string.help_url_adaptive_sleep; return R.string.help_url_adaptive_sleep;
@@ -352,10 +378,6 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
} }
} }
private static boolean isScreenAttentionAvailable(Context context) {
return AdaptiveSleepPreferenceController.isAdaptiveSleepSupported(context);
}
private static long getTimeoutFromKey(String key) { private static long getTimeoutFromKey(String key) {
return Long.parseLong(key); return Long.parseLong(key);
} }
@@ -423,7 +445,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment
new BaseSearchIndexProvider(R.xml.screen_timeout_settings) { new BaseSearchIndexProvider(R.xml.screen_timeout_settings) {
public List<SearchIndexableRaw> getRawDataToIndex( public List<SearchIndexableRaw> getRawDataToIndex(
Context context, boolean enabled) { Context context, boolean enabled) {
if (!isScreenAttentionAvailable(context)) { if (!isAdaptiveSleepSupported(context)) {
return null; return null;
} }
final Resources res = context.getResources(); final Resources res = context.getResources();

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.service.attention.AttentionService
fun Context.isAdaptiveSleepSupported() =
resources.getBoolean(com.android.internal.R.bool.config_adaptive_sleep_available) &&
isAttentionServiceAvailable()
private fun Context.isAttentionServiceAvailable(): Boolean {
val packageManager = getPackageManager()
val packageName = packageManager.attentionServicePackageName
if (packageName.isNullOrEmpty()) return false
val intent = Intent(AttentionService.SERVICE_INTERFACE).setPackage(packageName)
val resolveInfo = packageManager.resolveService(intent, PackageManager.MATCH_SYSTEM_ONLY)
return resolveInfo != null && resolveInfo.serviceInfo != null
}

View File

@@ -86,7 +86,13 @@ public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragme
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
if (isCatalystEnabled()) {
PreferenceScreen preferenceScreen = createPreferenceScreen();
setPreferenceScreen(preferenceScreen);
updateActivityTitleWithScreenTitle(preferenceScreen);
} else {
super.onCreatePreferences(savedInstanceState, rootKey); super.onCreatePreferences(savedInstanceState, rootKey);
}
try { try {
// Check if the xml specifies if static preferences should go on the top or bottom // Check if the xml specifies if static preferences should go on the top or bottom
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(getContext(), final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(getContext(),

View File

@@ -38,10 +38,13 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState; import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
@@ -51,7 +54,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accounts.AddAccountSettings; import com.android.settings.accounts.AddAccountSettings;
import com.android.settings.testutils.shadow.ShadowAuthenticationHelper; import com.android.settingslib.accounts.AuthenticatorHelper;
import com.android.settingslib.widget.SelectorWithWidgetPreference; import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before; import org.junit.Before;
@@ -66,22 +69,24 @@ import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAuthenticationHelper.class) @Config(shadows = ContactsStorageSettingsTest.ShadowAuthenticatorHelper.class)
public class ContactsStorageSettingsTest { public class ContactsStorageSettingsTest {
private static final String PREF_KEY_DEVICE_ONLY = "device_only_account_preference"; private static final String PREF_KEY_DEVICE_ONLY = "device_only_account_preference";
private static final String PREF_KEY_ACCOUNT_CATEGORY = "account_category"; private static final String PREF_KEY_ACCOUNT_CATEGORY = "account_category";
private static final String PREF_KEY_ADD_ACCOUNT = "add_account"; private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
private static final Account TEST_ACCOUNT1 = new Account("test@gmail.com", "type1"); private static final Account TEST_ACCOUNT1 = new Account("test@gmail.com", "com.google");
private static final Account TEST_ACCOUNT2 = new Account("test@samsung.com", "type2"); private static final Account TEST_ACCOUNT2 = new Account("test@samsung.com", "com.samsung");
private static final Account TEST_ACCOUNT3 = new Account("test@outlook.com", "type3"); private static final Account TEST_ACCOUNT3 = new Account("test@outlook.com", "com.outlook");
private static final Account SIM_ACCOUNT = new Account("SIM", "SIM"); private static final Account SIM_ACCOUNT = new Account("SIM", "SIM");
@@ -100,7 +105,8 @@ public class ContactsStorageSettingsTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mContactsStorageSettings = spy(new TestContactsStorageSettings(mContext, mContentResolver)); mContactsStorageSettings = spy(
new TestContactsStorageSettings(mContext, mContentResolver));
when(mContentResolver.acquireContentProviderClient( when(mContentResolver.acquireContentProviderClient(
eq(ContactsContract.AUTHORITY_URI))).thenReturn(mContentProviderClient); eq(ContactsContract.AUTHORITY_URI))).thenReturn(mContentProviderClient);
mPreferenceManager = new PreferenceManager(mContext); mPreferenceManager = new PreferenceManager(mContext);
@@ -116,6 +122,7 @@ public class ContactsStorageSettingsTest {
when(mContactsStorageSettings.findPreference(eq(PREF_KEY_ACCOUNT_CATEGORY))).thenReturn( when(mContactsStorageSettings.findPreference(eq(PREF_KEY_ACCOUNT_CATEGORY))).thenReturn(
accountCategory); accountCategory);
when(mContactsStorageSettings.getPreferenceScreen()).thenReturn(mScreen); when(mContactsStorageSettings.getPreferenceScreen()).thenReturn(mScreen);
mContactsStorageSettings.setEligibleAccountTypes(new String[]{"com.google"});
mContactsStorageSettings.onAttach(mContext); mContactsStorageSettings.onAttach(mContext);
} }
@@ -179,7 +186,6 @@ public class ContactsStorageSettingsTest {
new ArrayList<>()); new ArrayList<>());
when(mContentProviderClient.call(eq(QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD), any(), when(mContentProviderClient.call(eq(QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD), any(),
any())).thenReturn(eligibleAccountBundle); any())).thenReturn(eligibleAccountBundle);
mContactsStorageSettings.setEligibleAccountTypes(new String[]{"com.google"});
mContactsStorageSettings.refreshUI(); mContactsStorageSettings.refreshUI();
@@ -244,13 +250,13 @@ public class ContactsStorageSettingsTest {
SelectorWithWidgetPreference account1Preference = accountCategory.findPreference( SelectorWithWidgetPreference account1Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT1.hashCode())); String.valueOf(TEST_ACCOUNT1.hashCode()));
assertThat(account1Preference.getTitle()).isEqualTo("Device and LABEL1"); assertThat(account1Preference.getTitle()).isEqualTo("Device and Google");
assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com"); assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com");
assertThat(account1Preference.getIcon()).isNotNull(); assertThat(account1Preference.getIcon()).isNotNull();
SelectorWithWidgetPreference account2Preference = accountCategory.findPreference( SelectorWithWidgetPreference account2Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT2.hashCode())); String.valueOf(TEST_ACCOUNT2.hashCode()));
assertThat(account2Preference.getTitle()).isEqualTo("Device and LABEL2"); assertThat(account2Preference.getTitle()).isEqualTo("Device and Samsung");
assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com"); assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com");
assertThat(account2Preference.getIcon()).isNotNull(); assertThat(account2Preference.getIcon()).isNotNull();
@@ -265,7 +271,7 @@ public class ContactsStorageSettingsTest {
assertThat(setAccountBundle.getString(ContactsContract.Settings.ACCOUNT_NAME)).isEqualTo( assertThat(setAccountBundle.getString(ContactsContract.Settings.ACCOUNT_NAME)).isEqualTo(
"test@samsung.com"); "test@samsung.com");
assertThat(setAccountBundle.getString(ContactsContract.Settings.ACCOUNT_TYPE)).isEqualTo( assertThat(setAccountBundle.getString(ContactsContract.Settings.ACCOUNT_TYPE)).isEqualTo(
"type2"); "com.samsung");
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture()); verify(mContext).startActivity(intentCaptor.capture());
@@ -298,19 +304,19 @@ public class ContactsStorageSettingsTest {
SelectorWithWidgetPreference account1Preference = accountCategory.findPreference( SelectorWithWidgetPreference account1Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT1.hashCode())); String.valueOf(TEST_ACCOUNT1.hashCode()));
assertThat(account1Preference.getTitle()).isEqualTo("Device and LABEL1"); assertThat(account1Preference.getTitle()).isEqualTo("Device and Google");
assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com"); assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com");
assertThat(account1Preference.getIcon()).isNotNull(); assertThat(account1Preference.getIcon()).isNotNull();
SelectorWithWidgetPreference account2Preference = accountCategory.findPreference( SelectorWithWidgetPreference account2Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT2.hashCode())); String.valueOf(TEST_ACCOUNT2.hashCode()));
assertThat(account2Preference.getTitle()).isEqualTo("Device and LABEL2"); assertThat(account2Preference.getTitle()).isEqualTo("Device and Samsung");
assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com"); assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com");
assertThat(account2Preference.getIcon()).isNotNull(); assertThat(account2Preference.getIcon()).isNotNull();
SelectorWithWidgetPreference account3Preference = accountCategory.findPreference( SelectorWithWidgetPreference account3Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT3.hashCode())); String.valueOf(TEST_ACCOUNT3.hashCode()));
assertThat(account3Preference.getTitle()).isEqualTo("Device and LABEL3"); assertThat(account3Preference.getTitle()).isEqualTo("Device and Outlook");
assertThat(account3Preference.getSummary()).isEqualTo("test@outlook.com"); assertThat(account3Preference.getSummary()).isEqualTo("test@outlook.com");
assertThat(account3Preference.getIcon()).isNotNull(); assertThat(account3Preference.getIcon()).isNotNull();
@@ -345,6 +351,40 @@ public class ContactsStorageSettingsTest {
assertThat(simPreference.isChecked()).isTrue(); assertThat(simPreference.isChecked()).isTrue();
} }
@Test
public void verifyAccountPreference_newAccountAdded_accountAddedToAccountPreference()
throws Exception {
Bundle currentDefaultAccount = new Bundle();
currentDefaultAccount.putInt(KEY_DEFAULT_ACCOUNT_STATE,
DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD);
currentDefaultAccount.putString(ContactsContract.Settings.ACCOUNT_NAME, TEST_ACCOUNT1.name);
currentDefaultAccount.putString(ContactsContract.Settings.ACCOUNT_TYPE, TEST_ACCOUNT1.type);
when(mContentProviderClient.call(eq(QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD), any(),
any())).thenReturn(currentDefaultAccount);
Bundle eligibleAccountBundle = new Bundle();
ArrayList<Account> eligibleAccounts = new ArrayList<>(
List.of(TEST_ACCOUNT1, TEST_ACCOUNT2));
eligibleAccountBundle.putParcelableArrayList(KEY_ELIGIBLE_DEFAULT_ACCOUNTS,
eligibleAccounts);
when(mContentProviderClient.call(eq(QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD), any(),
any())).thenReturn(eligibleAccountBundle);
mContactsStorageSettings.onAccountsUpdate(null);
// onAccountsUpdate should refresh the icon and layouts.
SelectorWithWidgetPreference account1Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT1.hashCode()));
assertThat(account1Preference.getTitle()).isEqualTo("Device and Google");
assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com");
assertThat(account1Preference.getIcon()).isNotNull();
SelectorWithWidgetPreference account2Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT2.hashCode()));
assertThat(account2Preference.getTitle()).isEqualTo("Device and Samsung");
assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com");
assertThat(account2Preference.getIcon()).isNotNull();
}
@Test @Test
public void searchIndexProvider_shouldIndexResource() { public void searchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes = final List<SearchIndexableResource> indexRes =
@@ -388,4 +428,43 @@ public class ContactsStorageSettingsTest {
mEligibleAccountTypes = eligibleAccountTypes; mEligibleAccountTypes = eligibleAccountTypes;
} }
} }
@Implements(AuthenticatorHelper.class)
public static class ShadowAuthenticatorHelper {
boolean preloadDrawableForType = false;
@Implementation
public void listenToAccountUpdates() {
}
@Implementation
public void onAccountsUpdated(Account[] accounts) {
}
@Implementation
public void preloadDrawableForType(final Context context, final String accountType) {
preloadDrawableForType = true;
}
@Implementation
protected Drawable getDrawableForType(Context context, final String accountType) {
if (preloadDrawableForType) {
return context.getPackageManager().getDefaultActivityIcon();
}
return null;
}
@Implementation
protected CharSequence getLabelForType(Context context, final String accountType) {
if (TextUtils.equals(accountType, "com.google")) {
return "Google";
} else if (TextUtils.equals(accountType, "com.samsung")) {
return "Samsung";
} else if (TextUtils.equals(accountType, "com.outlook")) {
return "Outlook";
}
return null;
}
}
} }