Snap for 13197820 from 04cfbe0520 to 25Q2-release
Change-Id: I6242bf286aa706ff330efff71ff4ec2b60750d70
This commit is contained in:
@@ -1899,7 +1899,7 @@
|
||||
|
||||
<activity android:name="Settings$UserAspectRatioAppListActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/aspect_ratio_experimental_title">
|
||||
android:label="@string/aspect_ratio_title">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.MANAGE_USER_ASPECT_RATIO_SETTINGS"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -1912,7 +1912,7 @@
|
||||
|
||||
<activity android:name="Settings$UserAspectRatioAppActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/aspect_ratio_experimental_title">
|
||||
android:label="@string/aspect_ratio_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.MANAGE_USER_ASPECT_RATIO_SETTINGS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
@@ -58,7 +58,7 @@ flag {
|
||||
flag {
|
||||
name: "enable_remove_association_bt_unpair"
|
||||
is_exported: true
|
||||
namespace: "companion_device_manager"
|
||||
namespace: "companion"
|
||||
description: "Allow to disassociate when to forget a BT pair device"
|
||||
bug: "365613753"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2025 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.
|
||||
-->
|
||||
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:colorControlHighlight">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@color/settingslib_materialColorSecondaryContainer" />
|
||||
<corners
|
||||
android:radius="28dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
73
res/layout/bluetooth_device_spotlight_preference.xml
Normal file
73
res/layout/bluetooth_device_spotlight_preference.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2025 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.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="?android:attr/scrollbarSize"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:background="@drawable/device_details_spotlight_preference_background" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+android:id/icon"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="center"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView android:id="@+android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal" />
|
||||
|
||||
<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/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:maxLines="4" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -101,7 +101,8 @@
|
||||
android:hyphenationFrequency="normalFast"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Caption" />
|
||||
</LinearLayout>
|
||||
<Switch
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:theme="@style/Theme.Material3.DynamicColors.DayNight"
|
||||
android:id="@+id/phonebook_sharing_message_confirm_pin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
|
||||
@@ -13189,6 +13189,11 @@ Data usage charges may apply.</string>
|
||||
<!-- [CHAR LIMIT=200] Manage applications, text for dialog when killing persistent apps-->
|
||||
<string name="stop_app_dlg_text">Application will be stopped to apply page size compat setting.</string>
|
||||
|
||||
<!-- Error messages for 16 KB Developer option-->
|
||||
<string name="error_pending_updates">Kernel update failed. Check and install any pending updates.</string>
|
||||
|
||||
<string name="error_ota_failed">Kernel update failed. Error occurred while applying OTA.</string>
|
||||
|
||||
<!-- DSU Loader. Do not translate. -->
|
||||
|
||||
<string name="dsu_loader_title" translatable="false">DSU Loader</string>
|
||||
@@ -13836,20 +13841,6 @@ Data usage charges may apply.</string>
|
||||
<!-- [CHAR LIMIT=NONE] Warning description for app info aspect ratio page -->
|
||||
<string name="app_aspect_ratio_footer">The app will restart when you change aspect ratio. You may lose unsaved changes. Some apps may not be optimized for certain aspect ratios.</string>
|
||||
|
||||
<!-- TODO(b/300219974): Change aspect ratio title and clean up unused titles -->
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio experimental title settings to choose app aspect ratio -->
|
||||
<string name="aspect_ratio_experimental_title">Aspect ratio (experimental)</string>
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio experiment title settings to choose app aspect ratio -->
|
||||
<string name="aspect_ratio_experiment_title">Aspect ratio (experiment)</string>
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio labs title settings to choose app aspect ratio -->
|
||||
<string name="aspect_ratio_labs_title">Aspect ratio (labs)</string>
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio experimental title label -->
|
||||
<string name="aspect_ratio_experimental_label">Experimental</string>
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio experiment title label -->
|
||||
<string name="aspect_ratio_experiment_label">Experiment</string>
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio labs title label -->
|
||||
<string name="aspect_ratio_labs_label">Labs</string>
|
||||
|
||||
<!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_fingerprint_label">Fingerprint sensor</string>
|
||||
|
||||
@@ -14309,6 +14300,14 @@ Data usage charges may apply.</string>
|
||||
<string name="supervision_add_forgot_pin_preference_title">Forgot PIN</string>
|
||||
<!-- Title for web content filters entry [CHAR LIMIT=60] -->
|
||||
<string name="supervision_web_content_filters_title">Web content filters</string>
|
||||
<!-- Title for web content filters browser category [CHAR LIMIT=60] -->
|
||||
<string name="supervision_web_content_filters_browser_title">Google Chrome and Web</string>
|
||||
<!-- Title for web content filters browser category block explicit sites option [CHAR LIMIT=60] -->
|
||||
<string name="supervision_web_content_filters_browser_block_explicit_sites_title">Try to block explicit sites</string>
|
||||
<!-- Summary for web content filters browser category block explicit sites option [CHAR LIMIT=None] -->
|
||||
<string name="supervision_web_content_filters_browser_block_explicit_sites_summary">No filter is perfect, but this should help hide sexually explicit sites</string>
|
||||
<!-- Title for web content filters browser category allow all sites option [CHAR LIMIT=60] -->
|
||||
<string name="supervision_web_content_filters_browser_allow_all_sites_title">Allow all sites</string>
|
||||
<!-- Generic content description that is attached to the preview illustration at the top of an Accessibility feature toggle page. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_illustration_content_description"><xliff:g id="feature" example="Select to Speak">%1$s</xliff:g> animation</string>
|
||||
</resources>
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
<Preference
|
||||
android:key="aspect_ratio_apps"
|
||||
android:title="@string/aspect_ratio_experimental_title"
|
||||
android:title="@string/aspect_ratio_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:order="22"
|
||||
settings:controller="com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/aspect_ratio_experimental_title">
|
||||
android:title="@string/aspect_ratio_title">
|
||||
|
||||
<com.android.settingslib.widget.TopIntroPreference
|
||||
android:key="app_aspect_ratio_summary"
|
||||
|
||||
@@ -30,9 +30,6 @@ import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -45,6 +42,7 @@ import com.android.internal.accessibility.util.AccessibilityUtils;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
|
||||
import com.android.settings.accessibility.actionbar.FeedbackMenuController;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
@@ -105,8 +103,6 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
// presentation.
|
||||
private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000;
|
||||
|
||||
static final int MENU_ID_SEND_FEEDBACK = 0;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
private final Runnable mUpdateRunnable = new Runnable() {
|
||||
@@ -151,8 +147,6 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
|
||||
private AccessibilitySettingsContentObserver mSettingsContentObserver;
|
||||
|
||||
private FeedbackManager mFeedbackManager;
|
||||
|
||||
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
|
||||
new ArrayMap<>();
|
||||
private final List<Preference> mServicePreferences = new ArrayList<>();
|
||||
@@ -216,6 +210,7 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
mNeedPreferencesUpdate = false;
|
||||
registerContentMonitors();
|
||||
registerInputDeviceListener();
|
||||
FeedbackMenuController.init(this, SettingsEnums.ACCESSIBILITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,24 +247,6 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
if (getFeedbackManager().isAvailable()) {
|
||||
menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE,
|
||||
R.string.accessibility_send_feedback_title);
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == MENU_ID_SEND_FEEDBACK) {
|
||||
getFeedbackManager().sendFeedback();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_settings;
|
||||
@@ -280,18 +257,6 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setFeedbackManager(FeedbackManager feedbackManager) {
|
||||
this.mFeedbackManager = feedbackManager;
|
||||
}
|
||||
|
||||
private FeedbackManager getFeedbackManager() {
|
||||
if (mFeedbackManager == null) {
|
||||
mFeedbackManager = new FeedbackManager(getActivity(), SettingsEnums.ACCESSIBILITY);
|
||||
}
|
||||
return mFeedbackManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary for the current state of this accessibilityService.
|
||||
*
|
||||
|
||||
@@ -40,9 +40,6 @@ import android.service.quicksettings.TileService;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@@ -63,6 +60,7 @@ import com.android.internal.accessibility.common.ShortcutConstants;
|
||||
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.accessibility.actionbar.FeedbackMenuController;
|
||||
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.flags.Flags;
|
||||
@@ -94,7 +92,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
|
||||
private static final String IMG_PREFIX = "R.drawable.";
|
||||
private static final String DRAWABLE_FOLDER = "drawable";
|
||||
static final int MENU_ID_SEND_FEEDBACK = 0;
|
||||
|
||||
protected TopIntroPreference mTopIntroPreference;
|
||||
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
|
||||
@@ -108,7 +105,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
protected Intent mSettingsIntent;
|
||||
// The mComponentName maybe null, such as Magnify
|
||||
protected ComponentName mComponentName;
|
||||
@Nullable private FeedbackManager mFeedbackManager;
|
||||
protected CharSequence mFeatureName;
|
||||
protected Uri mImageUri;
|
||||
protected CharSequence mHtmlDescription;
|
||||
@@ -142,6 +138,8 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
|
||||
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
|
||||
registerKeysToObserverCallback(mSettingsContentObserver);
|
||||
|
||||
FeedbackMenuController.init(this, getFeedbackCategory());
|
||||
}
|
||||
|
||||
protected void registerKeysToObserverCallback(
|
||||
@@ -247,24 +245,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
removeActionBarToggleSwitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
if (getFeedbackManager().isAvailable()) {
|
||||
menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE,
|
||||
R.string.accessibility_send_feedback_title);
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == MENU_ID_SEND_FEEDBACK) {
|
||||
getFeedbackManager().sendFeedback();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDialogMetricsCategory(int dialogId) {
|
||||
switch (dialogId) {
|
||||
@@ -280,6 +260,18 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
return SettingsEnums.ACCESSIBILITY_SERVICE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category of the feedback page.
|
||||
*
|
||||
* <p>By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that
|
||||
* the feedback category is unknown, and the absence of a feedback menu.
|
||||
*
|
||||
* @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default.
|
||||
*/
|
||||
protected int getFeedbackCategory() {
|
||||
return SettingsEnums.PAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
return 0;
|
||||
@@ -785,28 +777,4 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
||||
super.onCreateRecyclerView(inflater, parent, savedInstanceState);
|
||||
return AccessibilityFragmentUtils.addCollectionInfoToAccessibilityDelegate(recyclerView);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setFeedbackManager(FeedbackManager feedbackManager) {
|
||||
this.mFeedbackManager = feedbackManager;
|
||||
}
|
||||
|
||||
private FeedbackManager getFeedbackManager() {
|
||||
if (mFeedbackManager == null) {
|
||||
mFeedbackManager = new FeedbackManager(getActivity(), getFeedbackCategory());
|
||||
}
|
||||
return mFeedbackManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category of the feedback page.
|
||||
*
|
||||
* <p>By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that
|
||||
* the feedback category is unknown, and the absence of a feedback menu.
|
||||
*
|
||||
* @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default.
|
||||
*/
|
||||
protected int getFeedbackCategory() {
|
||||
return SettingsEnums.PAGE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.android.settings.R
|
||||
import com.android.settings.contract.KEY_VIBRATION_HAPTICS
|
||||
import com.android.settings.metrics.PreferenceActionMetricsProvider
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSystemStore
|
||||
import com.android.settingslib.metadata.BooleanValuePreference
|
||||
import com.android.settingslib.metadata.PreferenceMetadata
|
||||
@@ -96,18 +96,13 @@ class VibrationMainSwitchPreference :
|
||||
class VibrationMainSwitchStore(
|
||||
context: Context,
|
||||
private val settingsStore: KeyValueStore = SettingsSystemStore.get(context),
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(key)
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = DEFAULT_VALUE as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
|
||||
settingsStore.getValue(key, valueType) ?: getDefaultValue(key, valueType)
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
|
||||
settingsStore.setValue(key, valueType, value)
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_VALUE = true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.accessibility.actionbar;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.FeedbackManager;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
|
||||
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
|
||||
|
||||
/**
|
||||
* A controller that adds feedback menu to any Settings page.
|
||||
*/
|
||||
public class FeedbackMenuController implements LifecycleObserver, OnCreateOptionsMenu,
|
||||
OnOptionsItemSelected {
|
||||
|
||||
/**
|
||||
* The menu item ID for the feedback menu option.
|
||||
*/
|
||||
public static final int MENU_FEEDBACK = Menu.FIRST + 10;
|
||||
|
||||
/**
|
||||
* The menu item ID for the feedback menu option.
|
||||
*/
|
||||
private final FeedbackManager mFeedbackManager;
|
||||
|
||||
/**
|
||||
* Initializes the FeedbackMenuController for an InstrumentedPreferenceFragment with a provided
|
||||
* pade ID.
|
||||
*
|
||||
* @param host The InstrumentedPreferenceFragment to which the menu controller will be added.
|
||||
* @param pageId The page ID used for feedback tracking.
|
||||
*/
|
||||
public static void init(@NonNull InstrumentedPreferenceFragment host, int pageId) {
|
||||
host.getSettingsLifecycle().addObserver(
|
||||
new FeedbackMenuController(
|
||||
new FeedbackManager(host.getActivity(), pageId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the FeedbackMenuController for an InstrumentedPreferenceFragment with a provided
|
||||
* FeedbackManager.
|
||||
*
|
||||
* @param host The InstrumentedPreferenceFragment to which the menu controller will be added.
|
||||
* @param feedbackManager The FeedbackManager to use for handling feedback actions.
|
||||
*/
|
||||
public static void init(@NonNull InstrumentedPreferenceFragment host,
|
||||
@NonNull FeedbackManager feedbackManager) {
|
||||
host.getSettingsLifecycle().addObserver(
|
||||
new FeedbackMenuController(feedbackManager));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
if (!mFeedbackManager.isAvailable()) {
|
||||
return;
|
||||
}
|
||||
menu.add(Menu.NONE, MENU_FEEDBACK, Menu.NONE, R.string.accessibility_send_feedback_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == MENU_FEEDBACK) {
|
||||
mFeedbackManager.sendFeedback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private FeedbackMenuController(@NonNull FeedbackManager feedbackManager) {
|
||||
mFeedbackManager = feedbackManager;
|
||||
}
|
||||
}
|
||||
@@ -204,5 +204,11 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric);
|
||||
new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric) {
|
||||
@Override
|
||||
protected boolean isPageSearchEnabled(Context context) {
|
||||
return super.isPageSearchEnabled(context)
|
||||
&& !Flags.biometricsOnboardingEducation();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
||||
import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils
|
||||
|
||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||
|
||||
class FaceEnroll: AppCompatActivity() {
|
||||
@@ -39,18 +38,33 @@ class FaceEnroll: AppCompatActivity() {
|
||||
private val enrollActivityProvider: FaceEnrollActivityClassProvider
|
||||
get() = featureFactory.faceFeatureProvider.enrollActivityClassProvider
|
||||
|
||||
private var isLaunched = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
/**
|
||||
* Logs the next activity to be launched, creates an intent for that activity,
|
||||
* adds flags to forward the result, includes any existing extras from the current intent,
|
||||
* starts the new activity and then finishes the current one
|
||||
*/
|
||||
Log.d("FaceEnroll", "forward to $nextActivityClass")
|
||||
val nextIntent = Intent(this, nextActivityClass)
|
||||
nextIntent.putExtras(intent)
|
||||
startActivityForResult(nextIntent, 0)
|
||||
if (savedInstanceState != null) {
|
||||
isLaunched = savedInstanceState.getBoolean(KEY_IS_LAUNCHED, isLaunched)
|
||||
}
|
||||
|
||||
if (!isLaunched) {
|
||||
/**
|
||||
* Logs the next activity to be launched, creates an intent for that activity,
|
||||
* adds flags to forward the result, includes any existing extras from the current intent,
|
||||
* starts the new activity and then finishes the current one
|
||||
*/
|
||||
Log.d("FaceEnroll", "forward to $nextActivityClass")
|
||||
val nextIntent = Intent(this, nextActivityClass)
|
||||
nextIntent.putExtras(intent)
|
||||
startActivityForResult(nextIntent, 0)
|
||||
|
||||
isLaunched = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(KEY_IS_LAUNCHED, isLaunched)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
@@ -60,6 +74,7 @@ class FaceEnroll: AppCompatActivity() {
|
||||
caller: ComponentCaller
|
||||
) {
|
||||
super.onActivityResult(requestCode, resultCode, data, caller)
|
||||
isLaunched = false
|
||||
if (intent.getBooleanExtra(
|
||||
CombinedBiometricStatusUtils.EXTRA_LAUNCH_FROM_SAFETY_SOURCE_ISSUE, false)
|
||||
&& resultCode != RESULT_FINISHED) {
|
||||
@@ -68,4 +83,8 @@ class FaceEnroll: AppCompatActivity() {
|
||||
setResult(resultCode, data)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val KEY_IS_LAUNCHED = "isLaunched"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,18 +62,33 @@ open class FingerprintEnroll: AppCompatActivity() {
|
||||
protected val enrollActivityProvider: FingerprintEnrollActivityClassProvider
|
||||
get() = featureFactory.fingerprintFeatureProvider.getEnrollActivityClassProvider(this)
|
||||
|
||||
private var isLaunched = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
/**
|
||||
* Logs the next activity to be launched, creates an intent for that activity,
|
||||
* adds flags to forward the result, includes any existing extras from the current intent,
|
||||
* starts the new activity and then finishes the current one
|
||||
*/
|
||||
Log.d("FingerprintEnroll", "forward to $nextActivityClass")
|
||||
val nextIntent = Intent(this, nextActivityClass)
|
||||
nextIntent.putExtras(intent)
|
||||
startActivityForResult(nextIntent, 0)
|
||||
if (savedInstanceState != null) {
|
||||
isLaunched = savedInstanceState.getBoolean(KEY_IS_LAUNCHED, isLaunched)
|
||||
}
|
||||
|
||||
if (!isLaunched) {
|
||||
/**
|
||||
* Logs the next activity to be launched, creates an intent for that activity,
|
||||
* adds flags to forward the result, includes any existing extras from the current intent,
|
||||
* starts the new activity and then finishes the current one
|
||||
*/
|
||||
Log.d("FingerprintEnroll", "forward to $nextActivityClass")
|
||||
val nextIntent = Intent(this, nextActivityClass)
|
||||
nextIntent.putExtras(intent)
|
||||
startActivityForResult(nextIntent, 0)
|
||||
|
||||
isLaunched = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(KEY_IS_LAUNCHED, isLaunched)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
@@ -83,6 +98,7 @@ open class FingerprintEnroll: AppCompatActivity() {
|
||||
caller: ComponentCaller
|
||||
) {
|
||||
super.onActivityResult(requestCode, resultCode, data, caller)
|
||||
isLaunched = false
|
||||
if (intent.getBooleanExtra(
|
||||
CombinedBiometricStatusUtils.EXTRA_LAUNCH_FROM_SAFETY_SOURCE_ISSUE, false)
|
||||
&& resultCode != BiometricEnrollBase.RESULT_FINISHED
|
||||
@@ -92,4 +108,8 @@ open class FingerprintEnroll: AppCompatActivity() {
|
||||
setResult(resultCode, data)
|
||||
finish()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val KEY_IS_LAUNCHED = "isLaunched"
|
||||
}
|
||||
}
|
||||
@@ -250,17 +250,19 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
|
||||
TextView messageViewCaptionHint = (TextView) view.findViewById(R.id.pin_values_hint);
|
||||
TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
|
||||
CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
|
||||
CheckBox contactSharing = (CheckBox) view.findViewById(
|
||||
R.id.phonebook_sharing_message_entry_pin);
|
||||
contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook));
|
||||
|
||||
CheckBox contactSharing =
|
||||
(CheckBox) view.findViewById(R.id.phonebook_sharing_message_entry_pin);
|
||||
if (contactSharing != null) {
|
||||
contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook));
|
||||
contactSharing.setVisibility(
|
||||
mPairingController.isContactSharingVisible() ? View.VISIBLE : View.GONE);
|
||||
mPairingController.setContactSharingState();
|
||||
contactSharing.setOnCheckedChangeListener(mPairingController);
|
||||
contactSharing.setChecked(mPairingController.getContactSharingState());
|
||||
}
|
||||
|
||||
EditText pairingView = (EditText) view.findViewById(R.id.text);
|
||||
|
||||
contactSharing.setVisibility(
|
||||
mPairingController.isContactSharingVisible() ? View.VISIBLE : View.GONE);
|
||||
mPairingController.setContactSharingState();
|
||||
contactSharing.setOnCheckedChangeListener(mPairingController);
|
||||
contactSharing.setChecked(mPairingController.getContactSharingState());
|
||||
|
||||
mPairingView = pairingView;
|
||||
|
||||
pairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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.bluetooth.ui.layout
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/** Represent the layout of device settings. */
|
||||
data class DeviceSettingLayout(val rows: List<DeviceSettingLayoutRow>)
|
||||
|
||||
/** Represent a row in the layout. */
|
||||
data class DeviceSettingLayoutRow(val columns: Flow<List<DeviceSettingLayoutColumn>>)
|
||||
|
||||
/** Represent a column in a row. */
|
||||
data class DeviceSettingLayoutColumn(val settingId: Int, val highlighted: Boolean)
|
||||
@@ -21,35 +21,26 @@ import android.app.settings.SettingsEnums
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import androidx.preference.TwoStatePreference
|
||||
import com.android.settings.R
|
||||
import com.android.settings.bluetooth.BlockingPrefWithSliceController
|
||||
import com.android.settings.bluetooth.BluetoothDetailsProfilesController
|
||||
import com.android.settings.bluetooth.ui.composable.Icon
|
||||
import com.android.settings.bluetooth.ui.composable.MultiTogglePreference
|
||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
|
||||
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
||||
import com.android.settings.bluetooth.ui.view.DeviceDetailsMoreSettingsFragment.Companion.KEY_DEVICE_ADDRESS
|
||||
@@ -58,6 +49,7 @@ import com.android.settings.core.SubSettingLauncher
|
||||
import com.android.settings.dashboard.DashboardFragment
|
||||
import com.android.settings.overlay.FeatureFactory
|
||||
import com.android.settings.spa.preference.ComposePreference
|
||||
import com.android.settingslib.PrimarySwitchPreference
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
|
||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingActionModel
|
||||
@@ -67,24 +59,15 @@ import com.android.settingslib.core.AbstractPreferenceController
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.preference.Preference as SpaPreference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
|
||||
import com.android.settingslib.spa.widget.ui.Footer
|
||||
import com.android.settingslib.widget.FooterPreference
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -105,7 +88,7 @@ interface DeviceDetailsFragmentFormatter {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DeviceDetailsFragmentFormatterImpl(
|
||||
private val context: Context,
|
||||
private val fragment: DashboardFragment,
|
||||
private val dashboardFragment: DashboardFragment,
|
||||
controllers: List<AbstractPreferenceController>,
|
||||
private val bluetoothAdapter: BluetoothAdapter,
|
||||
private val cachedDevice: CachedBluetoothDevice,
|
||||
@@ -120,32 +103,30 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
|
||||
private val viewModel: BluetoothDeviceDetailsViewModel =
|
||||
ViewModelProvider(
|
||||
fragment,
|
||||
BluetoothDeviceDetailsViewModel.Factory(
|
||||
fragment.requireActivity().application,
|
||||
bluetoothAdapter,
|
||||
cachedDevice,
|
||||
backgroundCoroutineContext,
|
||||
),
|
||||
)
|
||||
dashboardFragment,
|
||||
BluetoothDeviceDetailsViewModel.Factory(
|
||||
dashboardFragment.requireActivity().application,
|
||||
bluetoothAdapter,
|
||||
cachedDevice,
|
||||
backgroundCoroutineContext,
|
||||
),
|
||||
)
|
||||
.get(BluetoothDeviceDetailsViewModel::class.java)
|
||||
|
||||
/** Updates bluetooth device details fragment layout. */
|
||||
override fun updateLayout(fragmentType: FragmentTypeModel) {
|
||||
fragment.setLoading(true, false)
|
||||
dashboardFragment.setLoading(true, false)
|
||||
isLoading = true
|
||||
fragment.lifecycleScope.launch { updateLayoutInternal(fragmentType) }
|
||||
dashboardFragment.lifecycleScope.launch { updateLayoutInternal(fragmentType) }
|
||||
}
|
||||
|
||||
private suspend fun updateLayoutInternal(fragmentType: FragmentTypeModel) {
|
||||
val items = viewModel.getItems(fragmentType) ?: run {
|
||||
fragment.setLoading(false, false)
|
||||
return
|
||||
}
|
||||
val layout = viewModel.getLayout(fragmentType) ?: run {
|
||||
fragment.setLoading(false, false)
|
||||
return
|
||||
}
|
||||
val items =
|
||||
viewModel.getItems(fragmentType)
|
||||
?: run {
|
||||
dashboardFragment.setLoading(false, false)
|
||||
return
|
||||
}
|
||||
|
||||
val prefKeyToSettingId =
|
||||
items
|
||||
@@ -153,14 +134,14 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
.associateBy({ it.preferenceKey }, { it.settingId })
|
||||
|
||||
val settingIdToXmlPreferences: MutableMap<Int, Preference> = HashMap()
|
||||
for (i in 0 until fragment.preferenceScreen.preferenceCount) {
|
||||
val pref = fragment.preferenceScreen.getPreference(i)
|
||||
for (i in 0 until dashboardFragment.preferenceScreen.preferenceCount) {
|
||||
val pref = dashboardFragment.preferenceScreen.getPreference(i)
|
||||
prefKeyToSettingId[pref.key]?.let { id -> settingIdToXmlPreferences[id] = pref }
|
||||
if (pref.key !in prefKeyToSettingId) {
|
||||
getController(pref.key)?.let { disableController(it) }
|
||||
}
|
||||
}
|
||||
fragment.preferenceScreen.removeAll()
|
||||
dashboardFragment.preferenceScreen.removeAll()
|
||||
for (job in prefVisibilityJobs) {
|
||||
job.cancel()
|
||||
}
|
||||
@@ -170,53 +151,83 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
val settingId = settingItem.settingId
|
||||
if (settingIdToXmlPreferences.containsKey(settingId)) {
|
||||
val pref = settingIdToXmlPreferences[settingId]!!.apply { order = row }
|
||||
fragment.preferenceScreen.addPreference(pref)
|
||||
dashboardFragment.preferenceScreen.addPreference(pref)
|
||||
} else {
|
||||
val prefKey = getPreferenceKey(settingId)
|
||||
|
||||
prefVisibilityJobs.add(
|
||||
getDevicesSettingForRow(layout, row)
|
||||
.onEach { logItemShown(prefKey, it.isNotEmpty()) }
|
||||
.launchIn(fragment.lifecycleScope)
|
||||
viewModel
|
||||
.getDeviceSetting(cachedDevice, settingId)
|
||||
.onEach { logItemShown(prefKey, it != null) }
|
||||
.launchIn(dashboardFragment.lifecycleScope)
|
||||
)
|
||||
val pref =
|
||||
ComposePreference(context)
|
||||
.apply {
|
||||
key = prefKey
|
||||
order = row
|
||||
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_ANC) {
|
||||
// TODO(b/399316980): replace it with SegmentedButtonPreference once it's ready.
|
||||
val pref =
|
||||
ComposePreference(context)
|
||||
.apply {
|
||||
key = prefKey
|
||||
order = row
|
||||
}
|
||||
.also { pref ->
|
||||
pref.setContent {
|
||||
buildComposePreference(cachedDevice, settingId, prefKey)
|
||||
}
|
||||
}
|
||||
dashboardFragment.preferenceScreen.addPreference(pref)
|
||||
} else {
|
||||
viewModel
|
||||
.getDeviceSetting(cachedDevice, settingId)
|
||||
.onEach {
|
||||
val existedPref =
|
||||
dashboardFragment.preferenceScreen.findPreference<Preference>(
|
||||
prefKey
|
||||
)
|
||||
val item =
|
||||
it
|
||||
?: run {
|
||||
existedPref?.let {
|
||||
dashboardFragment.preferenceScreen.removePreference(
|
||||
existedPref
|
||||
)
|
||||
}
|
||||
return@onEach
|
||||
}
|
||||
buildPreference(existedPref, item, prefKey, settingItem.highlighted)
|
||||
?.apply {
|
||||
key = prefKey
|
||||
order = row
|
||||
}
|
||||
?.also { dashboardFragment.preferenceScreen.addPreference(it) }
|
||||
}
|
||||
.also { pref -> pref.setContent { buildPreference(layout, row, prefKey) } }
|
||||
fragment.preferenceScreen.addPreference(pref)
|
||||
.launchIn(dashboardFragment.lifecycleScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(b/343317785): figure out how to remove the foot preference.
|
||||
fragment.preferenceScreen.addPreference(ComposePreference(context).apply {
|
||||
order = 10000
|
||||
isEnabled = false
|
||||
isSelectable = false
|
||||
setContent { Spacer(modifier = Modifier.height(1.dp)) }
|
||||
})
|
||||
|
||||
for (row in items.indices) {
|
||||
val settingItem = items[row]
|
||||
val settingId = settingItem.settingId
|
||||
if (settingIdToXmlPreferences.containsKey(settingId)) {
|
||||
val pref = fragment.preferenceScreen.getPreference(row)
|
||||
settingIdToXmlPreferences[settingId]?.let { pref ->
|
||||
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES) {
|
||||
(getController(pref.key) as? BluetoothDetailsProfilesController)?.run {
|
||||
if (settingItem is DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem) {
|
||||
if (
|
||||
settingItem
|
||||
is DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem
|
||||
) {
|
||||
setInvisibleProfiles(settingItem.invisibleProfiles)
|
||||
setHasExtraSpace(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
getController(pref.key)?.displayPreference(fragment.preferenceScreen)
|
||||
getController(pref.key)?.displayPreference(dashboardFragment.preferenceScreen)
|
||||
logItemShown(pref.key, pref.isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
fragment.lifecycleScope.launch {
|
||||
dashboardFragment.lifecycleScope.launch {
|
||||
if (isLoading) {
|
||||
fragment.setLoading(false, false)
|
||||
dashboardFragment.setLoading(false, false)
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
@@ -236,87 +247,170 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
} ?: emit(null)
|
||||
}
|
||||
|
||||
private fun getDevicesSettingForRow(
|
||||
layout: DeviceSettingLayout,
|
||||
row: Int,
|
||||
): Flow<List<DeviceSettingPreferenceModel>> =
|
||||
layout.rows[row].columns.flatMapLatest { columns ->
|
||||
if (columns.isEmpty()) {
|
||||
flowOf(emptyList())
|
||||
} else {
|
||||
combine(
|
||||
columns.map { column ->
|
||||
viewModel.getDeviceSetting(cachedDevice, column.settingId)
|
||||
}
|
||||
) {
|
||||
it.toList().filterNotNull()
|
||||
private fun buildPreference(
|
||||
existedPref: Preference?,
|
||||
model: DeviceSettingPreferenceModel,
|
||||
prefKey: String,
|
||||
highlighted: Boolean,
|
||||
): Preference? =
|
||||
when (model) {
|
||||
is DeviceSettingPreferenceModel.PlainPreference -> {
|
||||
val pref =
|
||||
existedPref
|
||||
?: run {
|
||||
if (highlighted) SpotlightPreference(context) else Preference(context)
|
||||
}
|
||||
pref.apply {
|
||||
title = model.title
|
||||
summary = model.summary
|
||||
icon = getDrawable(model.icon)
|
||||
onPreferenceClickListener =
|
||||
object : Preference.OnPreferenceClickListener {
|
||||
override fun onPreferenceClick(p: Preference): Boolean {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
model.action?.let { triggerAction(it) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DeviceSettingPreferenceModel.SwitchPreference ->
|
||||
if (model.action == null) {
|
||||
val pref =
|
||||
existedPref as? SwitchPreferenceCompat ?: SwitchPreferenceCompat(context)
|
||||
pref.apply {
|
||||
title = model.title
|
||||
summary = model.summary
|
||||
icon = getDrawable(model.icon)
|
||||
isChecked = model.checked
|
||||
isEnabled = !model.disabled
|
||||
onPreferenceChangeListener =
|
||||
object : Preference.OnPreferenceChangeListener {
|
||||
override fun onPreferenceChange(
|
||||
p: Preference,
|
||||
value: Any?,
|
||||
): Boolean {
|
||||
(p as? TwoStatePreference)?.let { newState ->
|
||||
val newState = value as? Boolean ?: return false
|
||||
logItemClick(
|
||||
prefKey,
|
||||
if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF,
|
||||
)
|
||||
isEnabled = false
|
||||
model.onCheckedChange.invoke(newState)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val pref =
|
||||
existedPref as? PrimarySwitchPreference ?: PrimarySwitchPreference(context)
|
||||
pref.apply {
|
||||
title = model.title
|
||||
summary = model.summary
|
||||
icon = getDrawable(model.icon)
|
||||
isChecked = model.checked
|
||||
isEnabled = !model.disabled
|
||||
isSwitchEnabled = !model.disabled
|
||||
onPreferenceClickListener =
|
||||
object : Preference.OnPreferenceClickListener {
|
||||
override fun onPreferenceClick(p: Preference): Boolean {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
triggerAction(model.action)
|
||||
return true
|
||||
}
|
||||
}
|
||||
onPreferenceChangeListener =
|
||||
object : Preference.OnPreferenceChangeListener {
|
||||
override fun onPreferenceChange(
|
||||
p: Preference,
|
||||
value: Any?,
|
||||
): Boolean {
|
||||
val newState = value as? Boolean ?: return false
|
||||
logItemClick(
|
||||
prefKey,
|
||||
if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF,
|
||||
)
|
||||
isSwitchEnabled = false
|
||||
model.onCheckedChange.invoke(newState)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is DeviceSettingPreferenceModel.MultiTogglePreference -> {
|
||||
// TODO(b/399316980): implemented it by SegmentedButtonPreference
|
||||
null
|
||||
}
|
||||
is DeviceSettingPreferenceModel.FooterPreference -> {
|
||||
val pref = existedPref as? FooterPreference ?: FooterPreference(context)
|
||||
pref.apply { title = model.footerText }
|
||||
}
|
||||
is DeviceSettingPreferenceModel.MoreSettingsPreference -> {
|
||||
val pref = existedPref ?: Preference(context)
|
||||
pref.apply {
|
||||
title =
|
||||
context.getString(R.string.bluetooth_device_more_settings_preference_title)
|
||||
summary =
|
||||
context.getString(
|
||||
R.string.bluetooth_device_more_settings_preference_summary
|
||||
)
|
||||
icon = context.getDrawable(R.drawable.ic_chevron_right_24dp)
|
||||
onPreferenceClickListener =
|
||||
object : Preference.OnPreferenceClickListener {
|
||||
override fun onPreferenceClick(p: Preference): Boolean {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
SubSettingLauncher(context)
|
||||
.setDestination(
|
||||
DeviceDetailsMoreSettingsFragment::class.java.name
|
||||
)
|
||||
.setSourceMetricsCategory(
|
||||
dashboardFragment.getMetricsCategory()
|
||||
)
|
||||
.setArguments(
|
||||
Bundle().apply {
|
||||
putString(KEY_DEVICE_ADDRESS, cachedDevice.address)
|
||||
}
|
||||
)
|
||||
.launch()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DeviceSettingPreferenceModel.HelpPreference -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun buildPreference(layout: DeviceSettingLayout, row: Int, prefKey: String) {
|
||||
val contents by
|
||||
remember(row) { getDevicesSettingForRow(layout, row) }
|
||||
.collectAsStateWithLifecycle(initialValue = listOf())
|
||||
|
||||
val highlighted by
|
||||
remember(row) {
|
||||
layout.rows[row].columns.map { columns -> columns.any { it.highlighted } }
|
||||
private fun getDrawable(deviceSettingIcon: DeviceSettingIcon?): Drawable? =
|
||||
when (deviceSettingIcon) {
|
||||
is DeviceSettingIcon.BitmapIcon ->
|
||||
deviceSettingIcon.bitmap.toDrawable(context.resources)
|
||||
is DeviceSettingIcon.ResourceIcon -> context.getDrawable(deviceSettingIcon.resId)
|
||||
null -> null
|
||||
}
|
||||
.collectAsStateWithLifecycle(initialValue = false)
|
||||
|
||||
@Composable
|
||||
private fun buildComposePreference(
|
||||
cachedDevice: CachedBluetoothDevice,
|
||||
settingId: Int,
|
||||
prefKey: String,
|
||||
) {
|
||||
val contents by
|
||||
remember(settingId) { viewModel.getDeviceSetting(cachedDevice, settingId) }
|
||||
.collectAsStateWithLifecycle(initialValue = null)
|
||||
|
||||
val settings = contents
|
||||
AnimatedVisibility(visible = settings.isNotEmpty(), enter = fadeIn(), exit = fadeOut()) {
|
||||
Box {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.matchParentSize()
|
||||
.padding(16.dp, 0.dp, 8.dp, 0.dp)
|
||||
.background(
|
||||
color =
|
||||
if (highlighted) {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
} else {
|
||||
Color.Transparent
|
||||
},
|
||||
shape = RoundedCornerShape(28.dp),
|
||||
)
|
||||
) {}
|
||||
buildPreferences(settings, prefKey)
|
||||
AnimatedVisibility(visible = settings != null, enter = fadeIn(), exit = fadeOut()) {
|
||||
(settings as? DeviceSettingPreferenceModel.MultiTogglePreference)?.let {
|
||||
buildMultiTogglePreference(it, prefKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun buildPreferences(settings: List<DeviceSettingPreferenceModel?>, prefKey: String) {
|
||||
when (settings.size) {
|
||||
0 -> {}
|
||||
1 -> {
|
||||
when (val setting = settings[0]) {
|
||||
is DeviceSettingPreferenceModel.PlainPreference -> {
|
||||
buildPlainPreference(setting, prefKey)
|
||||
}
|
||||
is DeviceSettingPreferenceModel.SwitchPreference -> {
|
||||
buildSwitchPreference(setting, prefKey)
|
||||
}
|
||||
is DeviceSettingPreferenceModel.MultiTogglePreference -> {
|
||||
buildMultiTogglePreference(setting, prefKey)
|
||||
}
|
||||
is DeviceSettingPreferenceModel.FooterPreference -> {
|
||||
buildFooterPreference(setting)
|
||||
}
|
||||
is DeviceSettingPreferenceModel.MoreSettingsPreference -> {
|
||||
buildMoreSettingsPreference(prefKey)
|
||||
}
|
||||
is DeviceSettingPreferenceModel.HelpPreference -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun buildMultiTogglePreference(
|
||||
pref: DeviceSettingPreferenceModel.MultiTogglePreference,
|
||||
@@ -332,107 +426,6 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun buildSwitchPreference(
|
||||
model: DeviceSettingPreferenceModel.SwitchPreference,
|
||||
prefKey: String,
|
||||
) {
|
||||
val switchPrefModel =
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = model.title
|
||||
override val summary = { model.summary ?: "" }
|
||||
override val checked = { model.checked }
|
||||
override val onCheckedChange = { newState: Boolean ->
|
||||
logItemClick(prefKey, if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF)
|
||||
model.onCheckedChange(newState)
|
||||
}
|
||||
override val changeable = { !model.disabled }
|
||||
override val icon: (@Composable () -> Unit)?
|
||||
get() {
|
||||
if (model.icon == null) {
|
||||
return null
|
||||
}
|
||||
return { deviceSettingIcon(model.icon) }
|
||||
}
|
||||
}
|
||||
if (model.action != null) {
|
||||
TwoTargetSwitchPreference(
|
||||
switchPrefModel,
|
||||
primaryOnClick = {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
triggerAction(model.action)
|
||||
},
|
||||
primaryEnabled = { !model.disabled },
|
||||
)
|
||||
} else {
|
||||
SwitchPreference(switchPrefModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun buildPlainPreference(
|
||||
model: DeviceSettingPreferenceModel.PlainPreference,
|
||||
prefKey: String,
|
||||
) {
|
||||
SpaPreference(
|
||||
object : PreferenceModel {
|
||||
override val title = model.title
|
||||
override val summary = { model.summary ?: "" }
|
||||
override val onClick = {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
model.action?.let { triggerAction(it) }
|
||||
Unit
|
||||
}
|
||||
override val icon: (@Composable () -> Unit)?
|
||||
get() {
|
||||
if (model.icon == null) {
|
||||
return null
|
||||
}
|
||||
return { deviceSettingIcon(model.icon) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun buildMoreSettingsPreference(prefKey: String) {
|
||||
SpaPreference(
|
||||
object : PreferenceModel {
|
||||
override val title =
|
||||
stringResource(R.string.bluetooth_device_more_settings_preference_title)
|
||||
override val summary = {
|
||||
context.getString(R.string.bluetooth_device_more_settings_preference_summary)
|
||||
}
|
||||
override val onClick = {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
SubSettingLauncher(context)
|
||||
.setDestination(DeviceDetailsMoreSettingsFragment::class.java.name)
|
||||
.setSourceMetricsCategory(fragment.getMetricsCategory())
|
||||
.setArguments(
|
||||
Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) }
|
||||
)
|
||||
.launch()
|
||||
}
|
||||
override val icon =
|
||||
@Composable {
|
||||
deviceSettingIcon(
|
||||
DeviceSettingIcon.ResourceIcon(R.drawable.ic_chevron_right_24dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun buildFooterPreference(model: DeviceSettingPreferenceModel.FooterPreference) {
|
||||
Footer(footerText = model.footerText)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun deviceSettingIcon(icon: DeviceSettingIcon?) {
|
||||
icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) }
|
||||
}
|
||||
|
||||
private fun logItemClick(preferenceKey: String, value: Int = 0) {
|
||||
logAction(preferenceKey, SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_CLICKED, value)
|
||||
}
|
||||
@@ -452,7 +445,7 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
if (it) EVENT_VISIBLE else EVENT_INVISIBLE,
|
||||
)
|
||||
}
|
||||
.launchIn(fragment.lifecycleScope)
|
||||
.launchIn(dashboardFragment.lifecycleScope)
|
||||
}
|
||||
}
|
||||
.value = visible
|
||||
@@ -485,7 +478,7 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
|
||||
private fun disableController(controller: AbstractPreferenceController) {
|
||||
if (controller is LifecycleObserver) {
|
||||
fragment.settingsLifecycle.removeObserver(controller as LifecycleObserver)
|
||||
dashboardFragment.settingsLifecycle.removeObserver(controller as LifecycleObserver)
|
||||
}
|
||||
|
||||
if (controller is BlockingPrefWithSliceController) {
|
||||
@@ -504,6 +497,19 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
|
||||
private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}"
|
||||
|
||||
private class SpotlightPreference(context: Context) : Preference(context) {
|
||||
|
||||
init {
|
||||
layoutResource = R.layout.bluetooth_device_spotlight_preference
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
holder.isDividerAllowedBelow = false
|
||||
holder.isDividerAllowedAbove = false
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TAG = "DeviceDetailsFormatter"
|
||||
const val EVENT_SWITCH_OFF = 0
|
||||
|
||||
@@ -23,9 +23,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.R
|
||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
|
||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutColumn
|
||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
|
||||
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||
@@ -39,11 +36,8 @@ import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class BluetoothDeviceDetailsViewModel(
|
||||
private val application: Application,
|
||||
@@ -141,43 +135,6 @@ class BluetoothDeviceDetailsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLayout(fragment: FragmentTypeModel): DeviceSettingLayout? {
|
||||
val configItems = getItems(fragment) ?: return null
|
||||
val idToDeviceSetting =
|
||||
configItems
|
||||
.filterIsInstance<DeviceSettingConfigItemModel.AppProvidedItem>()
|
||||
.associateBy({ it.settingId }, { getDeviceSetting(cachedDevice, it.settingId) })
|
||||
|
||||
val configDeviceSetting =
|
||||
configItems.map { idToDeviceSetting[it.settingId] ?: flowOf(null) }
|
||||
val positionToSettingIds =
|
||||
combine(configDeviceSetting) { settings ->
|
||||
val positionMapping = mutableMapOf<Int, List<DeviceSettingLayoutColumn>>()
|
||||
for (i in settings.indices) {
|
||||
val configItem = configItems[i]
|
||||
val setting = settings[i]
|
||||
val isXmlPreference = configItem is DeviceSettingConfigItemModel.BuiltinItem
|
||||
if (!isXmlPreference && setting == null) {
|
||||
continue
|
||||
}
|
||||
positionMapping[i] =
|
||||
listOf(
|
||||
DeviceSettingLayoutColumn(
|
||||
configItem.settingId,
|
||||
configItem.highlighted,
|
||||
)
|
||||
)
|
||||
}
|
||||
positionMapping
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = mapOf())
|
||||
return DeviceSettingLayout(
|
||||
configItems.indices.map { idx ->
|
||||
DeviceSettingLayoutRow(positionToSettingIds.map { it[idx] ?: emptyList() })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val application: Application,
|
||||
private val bluetoothAdapter: BluetoothAdapter,
|
||||
|
||||
@@ -103,7 +103,7 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag
|
||||
return false;
|
||||
}
|
||||
Lifecycle.State currentState = host.getLifecycle().getCurrentState();
|
||||
if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
if (!currentState.isAtLeast(Lifecycle.State.CREATED)) {
|
||||
Log.d(TAG, "Fail to show dialog with state: " + currentState);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment {
|
||||
return false;
|
||||
}
|
||||
Lifecycle.State currentState = host.getLifecycle().getCurrentState();
|
||||
if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
if (!currentState.isAtLeast(Lifecycle.State.CREATED)) {
|
||||
Log.d(TAG, "Fail to show dialog with state: " + currentState);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment {
|
||||
return false;
|
||||
}
|
||||
Lifecycle.State currentState = host.getLifecycle().getCurrentState();
|
||||
if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
if (!currentState.isAtLeast(Lifecycle.State.CREATED)) {
|
||||
Log.d(TAG, "Fail to show dialog with state: " + currentState);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,13 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
|
||||
public void onFailure(@NonNull Throwable t) {
|
||||
hideProgressDialog();
|
||||
Log.e(TAG, "Failed to call applyPayload of UpdateEngineStable!", t);
|
||||
displayToast(mContext.getString(R.string.toast_16k_update_failed_text));
|
||||
// installUpdate will always throw localized messages.
|
||||
String message = t.getMessage();
|
||||
if (message != null) {
|
||||
displayToast(message);
|
||||
} else {
|
||||
displayToast(mContext.getString(R.string.toast_16k_update_failed_text));
|
||||
}
|
||||
}
|
||||
},
|
||||
ContextCompat.getMainExecutor(mContext));
|
||||
@@ -208,10 +214,8 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
|
||||
int status = data.getInt(SystemUpdateManager.KEY_STATUS);
|
||||
if (status != SystemUpdateManager.STATUS_UNKNOWN
|
||||
&& status != SystemUpdateManager.STATUS_IDLE) {
|
||||
throw new RuntimeException(
|
||||
"System has pending update! Please restart the device to complete applying"
|
||||
+ " pending update. If you are seeing this after using 16KB developer"
|
||||
+ " options, please check configuration and OTA packages!");
|
||||
Log.e(TAG, "SystemUpdateManager is not available. Status :" + status);
|
||||
throw new RuntimeException(mContext.getString(R.string.error_pending_updates));
|
||||
}
|
||||
|
||||
// Publish system update info
|
||||
@@ -223,7 +227,11 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
|
||||
Log.i(TAG, "Update file path is " + updateFile.getAbsolutePath());
|
||||
applyUpdateFile(updateFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
Log.e(TAG, "Error occurred while applying OTA ", e);
|
||||
throw new RuntimeException(mContext.getString(R.string.error_ota_failed));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unknown error occurred while applying OTA ", e);
|
||||
throw new RuntimeException(mContext.getString(R.string.toast_16k_update_failed_text));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LinuxTerminalPreferenceController extends DeveloperOptionsPreferenc
|
||||
static final long MEMORY_MIN_BYTES = DataUnit.GIGABYTES.toBytes(4); // 4_000_000_000
|
||||
|
||||
@VisibleForTesting
|
||||
static final long STORAGE_MIN_BYTES = DataUnit.GIGABYTES.toBytes(64); // 64_000_000_000
|
||||
static final long STORAGE_MIN_BYTES = DataUnit.GIGABYTES.toBytes(16); // 16_000_000_000
|
||||
|
||||
private static final String LINUX_TERMINAL_KEY = "linux_terminal";
|
||||
|
||||
|
||||
@@ -33,9 +33,8 @@ import com.android.settings.metrics.PreferenceActionMetricsProvider
|
||||
import com.android.settings.restriction.PreferenceRestrictionMixin
|
||||
import com.android.settingslib.RestrictedSwitchPreference
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSecureStore
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.metadata.BooleanValuePreference
|
||||
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||
import com.android.settingslib.metadata.PreferenceLifecycleContext
|
||||
@@ -106,16 +105,14 @@ class AdaptiveSleepPreference :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class Storage(
|
||||
private val context: Context,
|
||||
private val settingsStore: SettingsStore = SettingsSecureStore.get(context),
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
private val settingsStore: KeyValueStore = SettingsSecureStore.get(context),
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(key)
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
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) {
|
||||
|
||||
@@ -23,8 +23,7 @@ import com.android.settings.Utils
|
||||
import com.android.settings.contract.KEY_BATTERY_PERCENTAGE
|
||||
import com.android.settings.metrics.PreferenceActionMetricsProvider
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSystemStore
|
||||
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||
import com.android.settingslib.metadata.ReadWritePermit
|
||||
@@ -71,17 +70,11 @@ class BatteryPercentageSwitchPreference :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class BatteryPercentageStorage(
|
||||
private val context: Context,
|
||||
private val settingsStore: SettingsStore,
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
private val settingsStore: KeyValueStore,
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(KEY)
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
|
||||
(settingsStore.getBoolean(key) ?: getDefaultValue(key, valueType)) as T
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
|
||||
settingsStore.setBoolean(key, value as Boolean)
|
||||
}
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
|
||||
context.resources.getBoolean(
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
|
||||
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
|
||||
import com.android.settingslib.search.SearchIndexableRaw;
|
||||
|
||||
@@ -47,8 +48,8 @@ public class DeviceStateAutoRotateSettingController extends TogglePreferenceCont
|
||||
|
||||
private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager;
|
||||
private final int mOrder;
|
||||
private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
|
||||
mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
|
||||
private final DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
|
||||
mDeviceStateAutoRotateSettingListener = () -> updateState(mPreference);
|
||||
private final int mDeviceState;
|
||||
private final String mDeviceStateDescription;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
@@ -77,12 +78,12 @@ public class DeviceStateAutoRotateSettingController extends TogglePreferenceCont
|
||||
|
||||
@OnLifecycleEvent(ON_START)
|
||||
void onStart() {
|
||||
mAutoRotateSettingsManager.registerListener(mDeviceStateRotationLockSettingsListener);
|
||||
mAutoRotateSettingsManager.registerListener(mDeviceStateAutoRotateSettingListener);
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_STOP)
|
||||
void onStop() {
|
||||
mAutoRotateSettingsManager.unregisterListener(mDeviceStateRotationLockSettingsListener);
|
||||
mAutoRotateSettingsManager.unregisterListener(mDeviceStateAutoRotateSettingListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
|
||||
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
|
||||
import com.android.settingslib.devicestate.SettableDeviceState;
|
||||
import com.android.settingslib.search.SearchIndexableRaw;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@@ -29,8 +29,7 @@ import com.android.settings.contract.KEY_SMOOTH_DISPLAY
|
||||
import com.android.settings.metrics.PreferenceActionMetricsProvider
|
||||
import com.android.settingslib.datastore.HandlerExecutor
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSystemStore
|
||||
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||
import com.android.settingslib.metadata.PreferenceLifecycleContext
|
||||
@@ -112,18 +111,16 @@ class PeakRefreshRateSwitchPreference :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class PeakRefreshRateStore(
|
||||
private val context: Context,
|
||||
private val settingsStore: SettingsStore,
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
private val settingsStore: KeyValueStore,
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(key)
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? {
|
||||
if (key != KEY) return super.getDefaultValue(key, valueType)
|
||||
return context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T
|
||||
}
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
|
||||
context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
|
||||
if (key != KEY) return null
|
||||
val refreshRate = settingsStore.getFloat(KEY) ?: context.defaultPeakRefreshRate
|
||||
return refreshRate.refreshRateAsBoolean(context) as T
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
|
||||
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
|
||||
|
||||
/**
|
||||
@@ -75,8 +76,8 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
||||
};
|
||||
|
||||
private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager;
|
||||
private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
|
||||
mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
|
||||
private final DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
|
||||
mDeviceStateAutoRotateSettingListener = () -> updateState(mPreference);
|
||||
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
|
||||
|
||||
public SmartAutoRotateController(Context context, String preferenceKey) {
|
||||
@@ -140,7 +141,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
||||
}
|
||||
RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener);
|
||||
mDeviceStateAutoRotateSettingsManager.registerListener(
|
||||
mDeviceStateRotationLockSettingsListener);
|
||||
mDeviceStateAutoRotateSettingListener);
|
||||
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
|
||||
}
|
||||
|
||||
@@ -152,7 +153,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
||||
mRotationPolicyListener = null;
|
||||
}
|
||||
mDeviceStateAutoRotateSettingsManager.unregisterListener(
|
||||
mDeviceStateRotationLockSettingsListener);
|
||||
mDeviceStateAutoRotateSettingListener);
|
||||
mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,8 @@ import com.android.settings.R
|
||||
import com.android.settings.contract.KEY_ADAPTIVE_CONNECTIVITY
|
||||
import com.android.settings.metrics.PreferenceActionMetricsProvider
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSecureStore
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.metadata.MainSwitchPreference
|
||||
import com.android.settingslib.metadata.ReadWritePermit
|
||||
import com.android.settingslib.metadata.SensitivityLevel
|
||||
@@ -42,7 +41,7 @@ class AdaptiveConnectivityTogglePreference :
|
||||
override fun tags(context: Context) = arrayOf(KEY_ADAPTIVE_CONNECTIVITY)
|
||||
|
||||
override fun storage(context: Context): KeyValueStore =
|
||||
AdaptiveConnectivityToggleStorage(context, SettingsSecureStore.get(context))
|
||||
AdaptiveConnectivityToggleStorage(context)
|
||||
|
||||
override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions()
|
||||
|
||||
@@ -64,20 +63,20 @@ class AdaptiveConnectivityTogglePreference :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class AdaptiveConnectivityToggleStorage(
|
||||
private val context: Context,
|
||||
private val settingsStore: SettingsStore,
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
private val settingsStore: KeyValueStore = SettingsSecureStore.get(context),
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(KEY)
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
|
||||
DEFAULT_VALUE as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
|
||||
(settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
|
||||
settingsStore.setBoolean(key, value as Boolean)
|
||||
context.getSystemService(WifiManager::class.java)?.setWifiScoringEnabled(value)
|
||||
settingsStore.setValue(key, valueType, value)
|
||||
context
|
||||
.getSystemService(WifiManager::class.java)
|
||||
?.setWifiScoringEnabled((value as Boolean?) ?: DEFAULT_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,8 @@ import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
|
||||
import com.android.settings.restriction.PreferenceRestrictionMixin
|
||||
import com.android.settingslib.RestrictedSwitchPreference
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsGlobalStore
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||
import com.android.settingslib.metadata.PreferenceLifecycleContext
|
||||
import com.android.settingslib.metadata.PreferenceLifecycleProvider
|
||||
@@ -92,17 +91,15 @@ class AirplaneModePreference :
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class AirplaneModeStorage(
|
||||
private val context: Context,
|
||||
private val settingsStore: SettingsStore = SettingsGlobalStore.get(context),
|
||||
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
private val settingsStore: KeyValueStore = SettingsGlobalStore.get(context),
|
||||
) : KeyValueStoreDelegate {
|
||||
|
||||
override fun contains(key: String) = settingsStore.contains(KEY)
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
|
||||
DEFAULT_VALUE as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>): T =
|
||||
(settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
|
||||
settingsStore.setValue(key, valueType, value)
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ import java.util.regex.Pattern;
|
||||
public class PrivateSpaceEducation extends InstrumentedFragment {
|
||||
private static final String TAG = "PrivateSpaceEducation";
|
||||
|
||||
private boolean mIsAnimationPlaying = true;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater,
|
||||
@@ -73,6 +75,7 @@ public class PrivateSpaceEducation extends InstrumentedFragment {
|
||||
.build());
|
||||
LottieAnimationView lottieAnimationView = rootView.findViewById(R.id.lottie_animation);
|
||||
LottieColorUtils.applyDynamicColors(getContext(), lottieAnimationView);
|
||||
lottieAnimationView.setOnClickListener(v -> handleAnimationClick(lottieAnimationView));
|
||||
|
||||
TextView infoTextView = rootView.findViewById(R.id.learn_more);
|
||||
Pattern pattern = Pattern.compile(infoTextView.getText().toString());
|
||||
@@ -110,4 +113,13 @@ public class PrivateSpaceEducation extends InstrumentedFragment {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void handleAnimationClick(LottieAnimationView lottieAnimationView) {
|
||||
if (mIsAnimationPlaying) {
|
||||
lottieAnimationView.pauseAnimation();
|
||||
} else {
|
||||
lottieAnimationView.playAnimation();
|
||||
}
|
||||
mIsAnimationPlaying = !mIsAnimationPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ public class PrivateSpaceSetLockFragment extends InstrumentedFragment {
|
||||
private static final String TAG = "PrivateSpaceSetLockFrag";
|
||||
private static final int HEADER_TEXT_MAX_LINES = 4;
|
||||
|
||||
private boolean mIsAnimationPlaying = true;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater,
|
||||
@@ -91,6 +93,7 @@ public class PrivateSpaceSetLockFragment extends InstrumentedFragment {
|
||||
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
|
||||
LottieAnimationView lottieAnimationView = rootView.findViewById(R.id.lottie_animation);
|
||||
LottieColorUtils.applyDynamicColors(getContext(), lottieAnimationView);
|
||||
lottieAnimationView.setOnClickListener(v -> handleAnimationClick(lottieAnimationView));
|
||||
|
||||
return rootView;
|
||||
}
|
||||
@@ -130,4 +133,13 @@ public class PrivateSpaceSetLockFragment extends InstrumentedFragment {
|
||||
Log.w(TAG, "Private profile user handle is null");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAnimationClick(LottieAnimationView lottieAnimationView) {
|
||||
if (mIsAnimationPlaying) {
|
||||
lottieAnimationView.pauseAnimation();
|
||||
} else {
|
||||
lottieAnimationView.playAnimation();
|
||||
}
|
||||
mIsAnimationPlaying = !mIsAnimationPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ import java.util.List;
|
||||
public class SetupSuccessFragment extends InstrumentedFragment {
|
||||
private static final String TAG = "SetupSuccessFragment";
|
||||
|
||||
private boolean mIsAnimationPlaying = true;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater,
|
||||
@@ -80,6 +82,7 @@ public class SetupSuccessFragment extends InstrumentedFragment {
|
||||
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
|
||||
LottieAnimationView lottieAnimationView = rootView.findViewById(R.id.lottie_animation);
|
||||
LottieColorUtils.applyDynamicColors(getContext(), lottieAnimationView);
|
||||
lottieAnimationView.setOnClickListener(v -> handleAnimationClick(lottieAnimationView));
|
||||
|
||||
return rootView;
|
||||
}
|
||||
@@ -141,4 +144,13 @@ public class SetupSuccessFragment extends InstrumentedFragment {
|
||||
task.finishAndRemoveTask();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAnimationClick(LottieAnimationView lottieAnimationView) {
|
||||
if (mIsAnimationPlaying) {
|
||||
lottieAnimationView.pauseAnimation();
|
||||
} else {
|
||||
lottieAnimationView.playAnimation();
|
||||
}
|
||||
mIsAnimationPlaying = !mIsAnimationPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,22 +18,21 @@ package com.android.settings.sound
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN
|
||||
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.SettingsSecureStore
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.metadata.ReadWritePermit
|
||||
import com.android.settingslib.metadata.SensitivityLevel
|
||||
import com.android.settingslib.metadata.SwitchPreference
|
||||
import com.android.settings.R
|
||||
|
||||
// LINT.IfChange
|
||||
class MediaControlsLockscreenSwitchPreference : SwitchPreference(
|
||||
KEY,
|
||||
R.string.media_controls_lockscreen_title,
|
||||
R.string.media_controls_lockscreen_description,
|
||||
) {
|
||||
class MediaControlsLockscreenSwitchPreference :
|
||||
SwitchPreference(
|
||||
KEY,
|
||||
R.string.media_controls_lockscreen_title,
|
||||
R.string.media_controls_lockscreen_description,
|
||||
) {
|
||||
|
||||
override val sensitivityLevel
|
||||
get() = SensitivityLevel.NO_SENSITIVITY
|
||||
@@ -52,21 +51,17 @@ class MediaControlsLockscreenSwitchPreference : SwitchPreference(
|
||||
MediaControlsLockscreenStore(SettingsSecureStore.get(context))
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class MediaControlsLockscreenStore(private val settingsStore: SettingsStore) :
|
||||
KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
override fun contains(key: String) = settingsStore.contains(key)
|
||||
private class MediaControlsLockscreenStore(private val settingsStore: KeyValueStore) :
|
||||
KeyValueStoreDelegate {
|
||||
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = true as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
|
||||
settingsStore.getValue(key, valueType) ?: getDefaultValue(key, valueType)
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
|
||||
settingsStore.setValue(key, valueType, value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY = MEDIA_CONTROLS_LOCK_SCREEN
|
||||
}
|
||||
}
|
||||
// LINT.ThenChange(MediaControlsLockScreenPreferenceController.java)
|
||||
// LINT.ThenChange(MediaControlsLockScreenPreferenceController.java)
|
||||
|
||||
@@ -17,21 +17,18 @@
|
||||
package com.android.settings.sound
|
||||
|
||||
import android.content.Context
|
||||
|
||||
import com.android.settings.R
|
||||
import com.android.settings.flags.Flags
|
||||
import com.android.settingslib.datastore.AbstractKeyedDataObservable
|
||||
import com.android.settingslib.datastore.HandlerExecutor
|
||||
import com.android.settingslib.datastore.KeyedObserver
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.KeyedObservableDelegate
|
||||
import com.android.settingslib.datastore.KeyValueStoreDelegate
|
||||
import com.android.settingslib.datastore.KeyedObserver
|
||||
import com.android.settingslib.datastore.SettingsSecureStore
|
||||
import com.android.settingslib.datastore.SettingsStore
|
||||
import com.android.settingslib.metadata.PreferenceChangeReason
|
||||
import com.android.settingslib.metadata.ProvidePreferenceScreen
|
||||
import com.android.settingslib.metadata.PreferenceSummaryProvider
|
||||
import com.android.settingslib.metadata.ProvidePreferenceScreen
|
||||
import com.android.settingslib.metadata.preferenceHierarchy
|
||||
|
||||
import com.android.settingslib.preference.PreferenceScreenCreator
|
||||
|
||||
// LINT.IfChange
|
||||
@@ -83,21 +80,16 @@ class MediaControlsScreen(context: Context) :
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MediaControlsStore(private val settingsStore: SettingsStore) :
|
||||
KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
|
||||
override fun contains(key: String) = settingsStore.contains(key)
|
||||
class MediaControlsStore(private val settingsStore: KeyValueStore) : KeyValueStoreDelegate {
|
||||
|
||||
override val keyValueStoreDelegate
|
||||
get() = settingsStore
|
||||
|
||||
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = true as T
|
||||
|
||||
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
|
||||
settingsStore.getValue(key, valueType) ?: getDefaultValue(key, valueType)
|
||||
|
||||
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
|
||||
settingsStore.setValue(key, valueType, value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY = "media_controls"
|
||||
}
|
||||
}
|
||||
// LINT.ThenChange(MediaControlsSettings.java)
|
||||
// LINT.ThenChange(MediaControlsSettings.java)
|
||||
|
||||
@@ -45,7 +45,7 @@ fun UserAspectRatioAppPreference(app: ApplicationInfo) {
|
||||
initialValue = stringResource(R.string.summary_placeholder),
|
||||
)
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = stringResource(R.string.aspect_ratio_experimental_title)
|
||||
override val title = stringResource(R.string.aspect_ratio_title)
|
||||
override val summary = { summary }
|
||||
override val onClick = presenter::startActivity
|
||||
})
|
||||
|
||||
@@ -85,7 +85,7 @@ object UserAspectRatioAppsPageProvider : SettingsPageProvider {
|
||||
fun EntryItem() {
|
||||
val summary = getSummary()
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = stringResource(R.string.aspect_ratio_experimental_title)
|
||||
override val title = stringResource(R.string.aspect_ratio_title)
|
||||
override val summary = { summary }
|
||||
override val onClick = navigator(name)
|
||||
})
|
||||
@@ -108,7 +108,7 @@ fun UserAspectRatioAppList(
|
||||
= { AppList() },
|
||||
) {
|
||||
AppListPage(
|
||||
title = stringResource(R.string.aspect_ratio_experimental_title),
|
||||
title = stringResource(R.string.aspect_ratio_title),
|
||||
listModel = rememberContext(::UserAspectRatioAppListModel),
|
||||
appList = appList,
|
||||
header = {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.supervision
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.Preference
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.datastore.KeyValueStore
|
||||
import com.android.settingslib.datastore.Permissions
|
||||
import com.android.settingslib.datastore.SettingsSecureStore
|
||||
import com.android.settingslib.metadata.BooleanValuePreference
|
||||
import com.android.settingslib.metadata.PreferenceMetadata
|
||||
import com.android.settingslib.metadata.ReadWritePermit
|
||||
import com.android.settingslib.metadata.SensitivityLevel
|
||||
import com.android.settingslib.preference.PreferenceBinding
|
||||
import com.android.settingslib.preference.forEachRecursively
|
||||
import com.android.settingslib.widget.SelectorWithWidgetPreference
|
||||
|
||||
/** Base class of web content filters Safe sites preferences. */
|
||||
sealed class SupervisionSafeSitesPreference :
|
||||
BooleanValuePreference, SelectorWithWidgetPreference.OnClickListener, PreferenceBinding {
|
||||
override fun storage(context: Context): KeyValueStore = SettingsSecureStore.get(context)
|
||||
|
||||
override fun getReadPermissions(context: Context) = Permissions.EMPTY
|
||||
|
||||
override fun getWritePermissions(context: Context) = Permissions.EMPTY
|
||||
|
||||
override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
|
||||
ReadWritePermit.ALLOW
|
||||
|
||||
override fun getWritePermit(
|
||||
context: Context,
|
||||
value: Boolean?,
|
||||
callingPid: Int,
|
||||
callingUid: Int,
|
||||
) = ReadWritePermit.DISALLOW
|
||||
|
||||
override val sensitivityLevel
|
||||
get() = SensitivityLevel.NO_SENSITIVITY
|
||||
|
||||
override fun createWidget(context: Context) = SelectorWithWidgetPreference(context)
|
||||
|
||||
override fun onRadioButtonClicked(emiter: SelectorWithWidgetPreference) {
|
||||
emiter.parent?.forEachRecursively {
|
||||
if (it is SelectorWithWidgetPreference) {
|
||||
it.isChecked = it == emiter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
|
||||
super.bind(preference, metadata)
|
||||
(preference as SelectorWithWidgetPreference).also {
|
||||
// TODO(b/401568468): Set the isChecked value using stored values.
|
||||
it.isChecked = (it.key == SupervisionAllowAllSitesPreference.KEY)
|
||||
it.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The "Try to block explicit sites" preference. */
|
||||
class SupervisionBlockExplicitSitesPreference : SupervisionSafeSitesPreference() {
|
||||
|
||||
override val key
|
||||
get() = KEY
|
||||
|
||||
override val title
|
||||
get() = R.string.supervision_web_content_filters_browser_block_explicit_sites_title
|
||||
|
||||
override val summary
|
||||
get() = R.string.supervision_web_content_filters_browser_block_explicit_sites_summary
|
||||
|
||||
companion object {
|
||||
const val KEY = "web_content_filters_browser_block_explicit_sites"
|
||||
}
|
||||
}
|
||||
|
||||
/** The "Allow all sites" preference. */
|
||||
class SupervisionAllowAllSitesPreference : SupervisionSafeSitesPreference() {
|
||||
|
||||
override val key
|
||||
get() = KEY
|
||||
|
||||
override val title
|
||||
get() = R.string.supervision_web_content_filters_browser_allow_all_sites_title
|
||||
|
||||
companion object {
|
||||
const val KEY = "web_content_filters_browser_allow_all_sites"
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.supervision
|
||||
import android.app.supervision.flags.Flags
|
||||
import android.content.Context
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.metadata.PreferenceCategory
|
||||
import com.android.settingslib.metadata.ProvidePreferenceScreen
|
||||
import com.android.settingslib.metadata.preferenceHierarchy
|
||||
import com.android.settingslib.preference.PreferenceScreenCreator
|
||||
@@ -41,10 +42,19 @@ class SupervisionWebContentFiltersScreen : PreferenceScreenCreator {
|
||||
|
||||
override fun getPreferenceHierarchy(context: Context) =
|
||||
preferenceHierarchy(context, this) {
|
||||
// TODO(b/395134536) implement the screen.
|
||||
+PreferenceCategory(
|
||||
BROWSER_RADIO_BUTTON_GROUP,
|
||||
R.string.supervision_web_content_filters_browser_title,
|
||||
) +=
|
||||
{
|
||||
+SupervisionBlockExplicitSitesPreference()
|
||||
+SupervisionAllowAllSitesPreference()
|
||||
}
|
||||
// TODO(b/401569571) implement the SafeSearch group.
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY = "supervision_web_content_filters"
|
||||
internal const val BROWSER_RADIO_BUTTON_GROUP = "browser_radio_button_group"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
@@ -46,8 +43,6 @@ import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.Settings;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -112,7 +107,6 @@ public class AccessibilitySettingsTest {
|
||||
private static final String EMPTY_STRING = "";
|
||||
private static final String DEFAULT_SUMMARY = "default summary";
|
||||
private static final String DEFAULT_DESCRIPTION = "default description";
|
||||
private static final String DEFAULT_CATEGORY = "default category";
|
||||
private static final String DEFAULT_LABEL = "default label";
|
||||
private static final Boolean SERVICE_ENABLED = true;
|
||||
private static final Boolean SERVICE_DISABLED = false;
|
||||
@@ -128,10 +122,6 @@ public class AccessibilitySettingsTest {
|
||||
private ShadowAccessibilityManager mShadowAccessibilityManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private Menu mMenu;
|
||||
@Mock
|
||||
private MenuItem mMenuItem;
|
||||
|
||||
private ActivityController<SettingsActivity> mActivityController;
|
||||
|
||||
@@ -451,58 +441,6 @@ public class AccessibilitySettingsTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() {
|
||||
setupFragment();
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
|
||||
|
||||
mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu).add(anyInt(), anyInt(), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() {
|
||||
setupFragment();
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
|
||||
|
||||
mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() {
|
||||
setupFragment();
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
|
||||
when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK);
|
||||
|
||||
mFragment.onOptionsItemSelected(mMenuItem);
|
||||
|
||||
Intent startedIntent = shadowOf(mFragment.getActivity()).getNextStartedActivity();
|
||||
assertThat(startedIntent).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() {
|
||||
setupFragment();
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
|
||||
when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK);
|
||||
|
||||
mFragment.onOptionsItemSelected(mMenuItem);
|
||||
|
||||
Intent startedIntent = shadowOf(mFragment.getActivity()).getNextStartedActivity();
|
||||
assertThat(startedIntent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
|
||||
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
@@ -82,6 +83,8 @@ public class ToggleColorInversionPreferenceFragmentTest {
|
||||
private PreferenceManager mPreferenceManager;
|
||||
@Mock
|
||||
private FragmentActivity mActivity;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
|
||||
@Before
|
||||
public void setUpTestFragment() {
|
||||
@@ -93,6 +96,7 @@ public class ToggleColorInversionPreferenceFragmentTest {
|
||||
when(mFragment.getContext()).thenReturn(mContext);
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
when(mActivity.getContentResolver()).thenReturn(mContext.getContentResolver());
|
||||
when(mActivity.getResources()).thenReturn(mResources);
|
||||
|
||||
mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
|
||||
when(mScreen.findPreference(mFragment.getUseServicePreferenceKey()))
|
||||
|
||||
@@ -23,8 +23,6 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -38,16 +36,14 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.icu.text.CaseMap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@@ -107,19 +103,10 @@ public class ToggleFeaturePreferenceFragmentTest {
|
||||
PLACEHOLDER_PACKAGE_NAME + "tile.placeholder";
|
||||
private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName(
|
||||
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME);
|
||||
private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT =
|
||||
PLACEHOLDER_PACKAGE_NAME + "tooltip_content";
|
||||
private static final String PLACEHOLDER_CATEGORY = "category";
|
||||
private static final String PLACEHOLDER_DIALOG_TITLE = "title";
|
||||
private static final String DEFAULT_SUMMARY = "default summary";
|
||||
private static final String DEFAULT_DESCRIPTION = "default description";
|
||||
private static final String DEFAULT_TOP_INTRO = "default top intro";
|
||||
|
||||
private static final String SOFTWARE_SHORTCUT_KEY =
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
||||
private static final String HARDWARE_SHORTCUT_KEY =
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
|
||||
|
||||
private TestToggleFeaturePreferenceFragment mFragment;
|
||||
@Spy
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@@ -135,9 +122,7 @@ public class ToggleFeaturePreferenceFragmentTest {
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private Menu mMenu;
|
||||
@Mock
|
||||
private MenuItem mMenuItem;
|
||||
private Resources mResources;
|
||||
|
||||
@Before
|
||||
public void setUpTestFragment() {
|
||||
@@ -150,6 +135,7 @@ public class ToggleFeaturePreferenceFragmentTest {
|
||||
when(mFragment.getContext()).thenReturn(mContext);
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
when(mActivity.getContentResolver()).thenReturn(mContentResolver);
|
||||
when(mActivity.getResources()).thenReturn(mResources);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
final PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
|
||||
when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
@@ -186,61 +172,6 @@ public class ToggleFeaturePreferenceFragmentTest {
|
||||
any(AccessibilitySettingsContentObserver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() {
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
|
||||
|
||||
mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu).add(anyInt(), eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK),
|
||||
anyInt(), eq(R.string.accessibility_send_feedback_title));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() {
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
|
||||
|
||||
mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu, never()).add(anyInt(),
|
||||
eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK), anyInt(),
|
||||
eq(R.string.accessibility_send_feedback_title));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() {
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
|
||||
when(mMenuItem.getItemId()).thenReturn(
|
||||
ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK);
|
||||
|
||||
mFragment.onOptionsItemSelected(mMenuItem);
|
||||
|
||||
verify(mActivity).startActivityForResult(
|
||||
argThat(intent -> intent != null
|
||||
&& Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() {
|
||||
mFragment.setFeedbackManager(
|
||||
new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
|
||||
when(mMenuItem.getItemId()).thenReturn(
|
||||
ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK);
|
||||
|
||||
mFragment.onOptionsItemSelected(mMenuItem);
|
||||
|
||||
verify(mActivity, never()).startActivityForResult(
|
||||
argThat(intent -> intent != null
|
||||
&& Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateShortcutPreferenceData_assignDefaultValueToVariable() {
|
||||
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.accessibility.actionbar;
|
||||
|
||||
import static com.android.settings.accessibility.actionbar.FeedbackMenuController.MENU_FEEDBACK;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Intent;
|
||||
import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.testing.EmptyFragmentActivity;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
|
||||
import com.android.settings.accessibility.FeedbackManager;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/** Tests for {@link FeedbackMenuController} */
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class FeedbackMenuControllerTest {
|
||||
private static final String PACKAGE_NAME = "com.android.test";
|
||||
private static final String DEFAULT_CATEGORY = "default category";
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMocks = MockitoJUnit.rule();
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
@Rule
|
||||
public ActivityScenarioRule<EmptyFragmentActivity> mActivityScenario =
|
||||
new ActivityScenarioRule<>(EmptyFragmentActivity.class);
|
||||
|
||||
private FragmentActivity mActivity;
|
||||
private InstrumentedPreferenceFragment mHost;
|
||||
private FeedbackManager mFeedbackManager;
|
||||
@Mock
|
||||
private Lifecycle mLifecycle;
|
||||
@Mock
|
||||
private Menu mMenu;
|
||||
@Mock
|
||||
private MenuItem mMenuItem;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mActivityScenario.getScenario().onActivity(activity -> mActivity = activity);
|
||||
mHost = spy(new InstrumentedPreferenceFragment() {
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
when(mHost.getActivity()).thenReturn(mActivity);
|
||||
when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem);
|
||||
when(mMenuItem.getItemId()).thenReturn(MENU_FEEDBACK);
|
||||
mFeedbackManager = new FeedbackManager(mActivity, PACKAGE_NAME, DEFAULT_CATEGORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void init_withPageId_shouldAttachToLifecycle() {
|
||||
when(mHost.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
|
||||
FeedbackMenuController.init(mHost, SettingsEnums.ACCESSIBILITY);
|
||||
|
||||
verify(mLifecycle).addObserver(any(FeedbackMenuController.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void init_withFeedbackManager_shouldAttachToLifecycle() {
|
||||
when(mHost.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
|
||||
FeedbackMenuController.init(mHost, mFeedbackManager);
|
||||
|
||||
verify(mLifecycle).addObserver(any(FeedbackMenuController.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() {
|
||||
FeedbackMenuController.init(mHost, mFeedbackManager);
|
||||
|
||||
mHost.getSettingsLifecycle().onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu).add(anyInt(), anyInt(), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() {
|
||||
FeedbackMenuController.init(mHost, mFeedbackManager);
|
||||
|
||||
mHost.getSettingsLifecycle().onCreateOptionsMenu(mMenu, /* inflater= */ null);
|
||||
|
||||
verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() {
|
||||
FeedbackMenuController.init(mHost, mFeedbackManager);
|
||||
|
||||
mHost.getSettingsLifecycle().onOptionsItemSelected(mMenuItem);
|
||||
|
||||
Intent intent = shadowOf(mActivity).getNextStartedActivity();
|
||||
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_BUG_REPORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
|
||||
public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() {
|
||||
FeedbackMenuController.init(mHost, mFeedbackManager);
|
||||
|
||||
mHost.getSettingsLifecycle().onOptionsItemSelected(mMenuItem);
|
||||
|
||||
Intent intent = shadowOf(mActivity).getNextStartedActivity();
|
||||
assertThat(intent).isNull();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.settings.bluetooth.ui.view
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.app.settings.SettingsEnums
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -25,6 +25,7 @@ import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
||||
@@ -33,6 +34,7 @@ import com.android.settings.testutils.FakeFeatureFactory
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
|
||||
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
|
||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingActionModel
|
||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
|
||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
|
||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
|
||||
@@ -58,14 +60,15 @@ import org.mockito.Mock
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.kotlin.doNothing
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.shadows.ShadowLooper
|
||||
import org.robolectric.shadows.ShadowLooper.shadowMainLooper
|
||||
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class DeviceDetailsFragmentFormatterTest {
|
||||
@@ -78,7 +81,7 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
@Mock private lateinit var headerController: AbstractPreferenceController
|
||||
@Mock private lateinit var buttonController: AbstractPreferenceController
|
||||
|
||||
private lateinit var context: Context
|
||||
@Spy private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private lateinit var fragment: TestFragment
|
||||
private lateinit var underTest: DeviceDetailsFragmentFormatter
|
||||
private lateinit var featureFactory: FakeFeatureFactory
|
||||
@@ -87,11 +90,15 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
featureFactory = FakeFeatureFactory.setupForTest()
|
||||
doNothing().`when`(context).startActivity(any(Intent::class.java))
|
||||
`when`(
|
||||
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
|
||||
eq(context), eq(bluetoothAdapter), any()))
|
||||
any(),
|
||||
eq(bluetoothAdapter),
|
||||
any(),
|
||||
)
|
||||
)
|
||||
.thenReturn(repository)
|
||||
fragmentActivity = Robolectric.setupActivity(FragmentActivity::class.java)
|
||||
assertThat(fragmentActivity.applicationContext).isNotNull()
|
||||
@@ -115,7 +122,8 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
listOf(profileController, headerController, buttonController),
|
||||
bluetoothAdapter,
|
||||
cachedDevice,
|
||||
testScope.testScheduler)
|
||||
testScope.testScheduler,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,11 +132,16 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||
.thenReturn(
|
||||
DeviceSettingConfigModel(
|
||||
listOf(), listOf(), DeviceSettingConfigItemModel.AppProvidedItem(12345, false)))
|
||||
val intent = Intent().apply {
|
||||
setAction(Intent.ACTION_VIEW)
|
||||
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
listOf(),
|
||||
listOf(),
|
||||
DeviceSettingConfigItemModel.AppProvidedItem(12345, false),
|
||||
)
|
||||
)
|
||||
val intent =
|
||||
Intent().apply {
|
||||
setAction(Intent.ACTION_VIEW)
|
||||
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
`when`(repository.getDeviceSetting(cachedDevice, 12345))
|
||||
.thenReturn(
|
||||
flowOf(
|
||||
@@ -136,12 +149,15 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
cachedDevice = cachedDevice,
|
||||
id = 12345,
|
||||
intent = intent,
|
||||
)))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
var helpPreference: DeviceSettingPreferenceModel.HelpPreference? = null
|
||||
underTest.getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment).onEach {
|
||||
helpPreference = it
|
||||
}.launchIn(testScope.backgroundScope)
|
||||
underTest
|
||||
.getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)
|
||||
.onEach { helpPreference = it }
|
||||
.launchIn(testScope.backgroundScope)
|
||||
delay(100)
|
||||
runCurrent()
|
||||
ShadowLooper.idleMainLooper()
|
||||
@@ -171,13 +187,19 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
listOf(
|
||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
||||
highlighted = false, preferenceKey = "bluetooth_device_header"),
|
||||
highlighted = false,
|
||||
preferenceKey = "bluetooth_device_header",
|
||||
),
|
||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||
DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES,
|
||||
highlighted = false, preferenceKey = "bluetooth_profiles"),
|
||||
highlighted = false,
|
||||
preferenceKey = "bluetooth_profiles",
|
||||
),
|
||||
),
|
||||
listOf(),
|
||||
null))
|
||||
null,
|
||||
)
|
||||
)
|
||||
|
||||
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
|
||||
runCurrent()
|
||||
@@ -189,13 +211,17 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
SettingsEnums.PAGE_UNKNOWN,
|
||||
SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN,
|
||||
0,
|
||||
"bluetooth_device_header", 1)
|
||||
"bluetooth_device_header",
|
||||
1,
|
||||
)
|
||||
verify(featureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
SettingsEnums.PAGE_UNKNOWN,
|
||||
SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN,
|
||||
0,
|
||||
"bluetooth_profiles", 1)
|
||||
"bluetooth_profiles",
|
||||
1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,16 +235,22 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
||||
highlighted = false,
|
||||
preferenceKey = "bluetooth_device_header"),
|
||||
preferenceKey = "bluetooth_device_header",
|
||||
),
|
||||
DeviceSettingConfigItemModel.AppProvidedItem(
|
||||
DeviceSettingId.DEVICE_SETTING_ID_ANC, highlighted = false),
|
||||
DeviceSettingId.DEVICE_SETTING_ID_ANC,
|
||||
highlighted = false,
|
||||
),
|
||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||
DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES,
|
||||
highlighted = false,
|
||||
preferenceKey = "bluetooth_profiles"),
|
||||
preferenceKey = "bluetooth_profiles",
|
||||
),
|
||||
),
|
||||
listOf(),
|
||||
null))
|
||||
null,
|
||||
)
|
||||
)
|
||||
`when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC))
|
||||
.thenReturn(
|
||||
flowOf(
|
||||
@@ -231,11 +263,17 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
ToggleModel(
|
||||
"",
|
||||
DeviceSettingIcon.BitmapIcon(
|
||||
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)))),
|
||||
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||
),
|
||||
)
|
||||
),
|
||||
isActive = true,
|
||||
state = DeviceSettingStateModel.MultiTogglePreferenceState(0),
|
||||
isAllowedChangingState = true,
|
||||
updateState = {})))
|
||||
updateState = {},
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
|
||||
runCurrent()
|
||||
@@ -244,13 +282,119 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
.containsExactly(
|
||||
"bluetooth_device_header",
|
||||
"DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}",
|
||||
"bluetooth_profiles")
|
||||
"bluetooth_profiles",
|
||||
)
|
||||
verify(featureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
SettingsEnums.PAGE_UNKNOWN,
|
||||
SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN,
|
||||
0,
|
||||
"DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", 1
|
||||
"DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}",
|
||||
1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateLayout_plainPreferenceClicked() {
|
||||
testScope.runTest {
|
||||
val settingId = 12345
|
||||
val intent = Intent("test_intent")
|
||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||
.thenReturn(
|
||||
DeviceSettingConfigModel(
|
||||
listOf(
|
||||
DeviceSettingConfigItemModel.AppProvidedItem(
|
||||
settingId,
|
||||
highlighted = false,
|
||||
)
|
||||
),
|
||||
listOf(),
|
||||
null,
|
||||
)
|
||||
)
|
||||
|
||||
`when`(repository.getDeviceSetting(cachedDevice, settingId))
|
||||
.thenReturn(
|
||||
flowOf(
|
||||
DeviceSettingModel.ActionSwitchPreference(
|
||||
cachedDevice = cachedDevice,
|
||||
id = settingId,
|
||||
title = "title",
|
||||
summary = "summary",
|
||||
icon = null,
|
||||
action = DeviceSettingActionModel.IntentAction(intent),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
|
||||
runCurrent()
|
||||
val displayedPrefs = getDisplayedPreferences()
|
||||
displayedPrefs[0].performClick()
|
||||
|
||||
assertThat(displayedPrefs).hasSize(1)
|
||||
verify(context).startActivity(intent)
|
||||
verify(featureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
SettingsEnums.PAGE_UNKNOWN,
|
||||
SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_CLICKED,
|
||||
0,
|
||||
"DEVICE_SETTING_$settingId",
|
||||
2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateLayout_switchPreferenceClicked() {
|
||||
val settingId = 12345
|
||||
testScope.runTest {
|
||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||
.thenReturn(
|
||||
DeviceSettingConfigModel(
|
||||
listOf(
|
||||
DeviceSettingConfigItemModel.AppProvidedItem(
|
||||
settingId,
|
||||
highlighted = false,
|
||||
)
|
||||
),
|
||||
listOf(),
|
||||
null,
|
||||
)
|
||||
)
|
||||
|
||||
`when`(repository.getDeviceSetting(cachedDevice, settingId))
|
||||
.thenReturn(
|
||||
flowOf(
|
||||
DeviceSettingModel.ActionSwitchPreference(
|
||||
cachedDevice = cachedDevice,
|
||||
id = settingId,
|
||||
title = "title",
|
||||
summary = "summary",
|
||||
icon = null,
|
||||
action = null,
|
||||
switchState = DeviceSettingStateModel.ActionSwitchPreferenceState(true),
|
||||
isAllowedChangingState = true,
|
||||
updateState = {},
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
|
||||
runCurrent()
|
||||
val displayedPrefs = getDisplayedPreferences()
|
||||
displayedPrefs[0].performClick()
|
||||
|
||||
assertThat(displayedPrefs).hasSize(1)
|
||||
assertThat(displayedPrefs[0]).isInstanceOf(SwitchPreferenceCompat::class.java)
|
||||
verify(featureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
SettingsEnums.PAGE_UNKNOWN,
|
||||
SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_CLICKED,
|
||||
0,
|
||||
"DEVICE_SETTING_$settingId",
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.app.Application
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.graphics.Bitmap
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
|
||||
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
||||
import com.android.settings.testutils.FakeFeatureFactory
|
||||
@@ -164,74 +163,6 @@ class BluetoothDeviceDetailsViewModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLayout_builtinDeviceSettings() {
|
||||
testScope.runTest {
|
||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||
.thenReturn(
|
||||
DeviceSettingConfigModel(
|
||||
listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), null))
|
||||
|
||||
val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
|
||||
|
||||
assertThat(getLatestLayout(layout))
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
listOf(DeviceSettingId.DEVICE_SETTING_ID_HEADER),
|
||||
listOf(DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS)))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLayout_remoteDeviceSettings() {
|
||||
val remoteSettingId1 = 10001
|
||||
val remoteSettingId2 = 10002
|
||||
val remoteSettingId3 = 10003
|
||||
testScope.runTest {
|
||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||
.thenReturn(
|
||||
DeviceSettingConfigModel(
|
||||
listOf(
|
||||
BUILTIN_SETTING_ITEM_1,
|
||||
buildRemoteSettingItem(remoteSettingId1),
|
||||
buildRemoteSettingItem(remoteSettingId2),
|
||||
buildRemoteSettingItem(remoteSettingId3),
|
||||
),
|
||||
listOf(),
|
||||
null))
|
||||
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
|
||||
.thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1)))
|
||||
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2))
|
||||
.thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId2)))
|
||||
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3))
|
||||
.thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3)))
|
||||
|
||||
val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
|
||||
|
||||
assertThat(getLatestLayout(layout))
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
listOf(DeviceSettingId.DEVICE_SETTING_ID_HEADER),
|
||||
listOf(remoteSettingId1),
|
||||
listOf(remoteSettingId2),
|
||||
listOf(remoteSettingId3),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLatestLayout(layout: DeviceSettingLayout): List<List<Int>> {
|
||||
val latestLayout = MutableList(layout.rows.size) { emptyList<Int>() }
|
||||
for (i in layout.rows.indices) {
|
||||
layout.rows[i]
|
||||
.columns
|
||||
.onEach { latestLayout[i] = it.map { c -> c.settingId } }
|
||||
.launchIn(testScope.backgroundScope)
|
||||
}
|
||||
|
||||
testScope.runCurrent()
|
||||
return latestLayout.filter { !it.isEmpty() }.toList()
|
||||
}
|
||||
|
||||
private fun buildMultiTogglePreference(settingId: Int) =
|
||||
DeviceSettingModel.MultiTogglePreference(
|
||||
cachedDevice,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.supervision
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SupervisionSafeSitesPreferenceTest {
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val allowAllSitesPreference = SupervisionAllowAllSitesPreference()
|
||||
|
||||
private val blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference()
|
||||
|
||||
@Test
|
||||
fun getTitle_allowAllSites() {
|
||||
assertThat(allowAllSitesPreference.title)
|
||||
.isEqualTo(R.string.supervision_web_content_filters_browser_allow_all_sites_title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTitle_blockExplicitSites() {
|
||||
assertThat(blockExplicitSitesPreference.title)
|
||||
.isEqualTo(R.string.supervision_web_content_filters_browser_block_explicit_sites_title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSummary_blockExplicitSites() {
|
||||
assertThat(blockExplicitSitesPreference.summary)
|
||||
.isEqualTo(
|
||||
R.string.supervision_web_content_filters_browser_block_explicit_sites_summary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ class UserAspectRatioAppPreferenceTest {
|
||||
|
||||
composeTestRule.onNode(
|
||||
hasTextExactly(
|
||||
context.getString(R.string.aspect_ratio_experimental_title),
|
||||
context.getString(R.string.aspect_ratio_title),
|
||||
context.getString(R.string.user_aspect_ratio_app_default)
|
||||
),
|
||||
).assertIsDisplayed().assertIsEnabled()
|
||||
|
||||
@@ -63,7 +63,7 @@ class UserAspectRatioAppsPageProviderTest {
|
||||
@Test
|
||||
fun injectEntry_title() {
|
||||
setInjectEntry()
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class UserAspectRatioAppsPageProviderTest {
|
||||
@Test
|
||||
fun injectEntry_onClick_navigate() {
|
||||
setInjectEntry()
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
|
||||
.performClick()
|
||||
assertThat(fakeNavControllerWrapper.navigateCalledWith).isEqualTo("UserAspectRatioAppsPage")
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class UserAspectRatioAppsPageProviderTest {
|
||||
UserAspectRatioAppList {}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user