diff --git a/res/drawable/screen_resolution_1080p.xml b/res/drawable/screen_resolution_full.xml
similarity index 100%
rename from res/drawable/screen_resolution_1080p.xml
rename to res/drawable/screen_resolution_full.xml
diff --git a/res/drawable/screen_resolution_1440p.xml b/res/drawable/screen_resolution_high.xml
similarity index 100%
rename from res/drawable/screen_resolution_1440p.xml
rename to res/drawable/screen_resolution_high.xml
diff --git a/res/layout/modifier_key_reset_dialog.xml b/res/layout/modifier_key_reset_dialog.xml
index fd38b11d5a8..11712bf9960 100644
--- a/res/layout/modifier_key_reset_dialog.xml
+++ b/res/layout/modifier_key_reset_dialog.xml
@@ -80,7 +80,7 @@
android:drawablePadding="9dp"
style="@style/ModifierKeyButtonCancel"
android:textColor="?android:attr/textColorPrimary"
- android:text="@string/modifier_keys_restore"/>
+ android:text="@string/modifier_keys_reset"/>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 959bc172793..aae7403c835 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -67,5 +67,9 @@
#7DA7F1
#607DA7F1
#FFEE675C
+
+
+
+ #FFFFFF
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 44f65034c70..f593db9e544 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4008,7 +4008,7 @@
Cancel
- Restore
+ Reset
Choose modifier key
@@ -4595,6 +4595,8 @@
One-handed mode added to Quick Settings. Swipe down to turn it on or off anytime.
You can also add one-handed mode to Quick Settings from the top of your screen
+
+ Font size added to Quick Settings. Swipe down to change the font size anytime.
Dismiss
@@ -11970,6 +11972,8 @@
On / Camera and screen flash
Flash the camera light or the screen when you receive notifications or when alarms sound
+
+ Flash the screen when you receive notifications or when alarms sound
Use flash notifications with caution if you\u0027re light sensitive
diff --git a/res/xml/flash_notifications_settings.xml b/res/xml/flash_notifications_settings.xml
index 0017fa605f5..243fffc02a8 100644
--- a/res/xml/flash_notifications_settings.xml
+++ b/res/xml/flash_notifications_settings.xml
@@ -21,12 +21,12 @@
+ settings:controller="com.android.settings.accessibility.FlashNotificationsIntroPreferenceController" />
+ settings:lottie_rawRes="@drawable/flash_notifications_illustration" />
mSizeData;
+ private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private boolean mSeekByTouch;
private Optional mInteractionListener = Optional.empty();
private LabeledSeekBarPreference mSeekBarPreference;
+ private int mLastProgress;
+ private boolean mNeedsQSTooltipReshow = false;
+ private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
+ private final Handler mHandler;
+
private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
new SeekBar.OnSeekBarChangeListener() {
@@ -54,6 +69,7 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
if (!mSeekByTouch) {
interactionListener.onProgressChanged();
+ onProgressFinalized();
}
}
@@ -67,6 +83,7 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
mSeekByTouch = false;
mInteractionListener.ifPresent(ProgressInteractionListener::onEndTrackingTouch);
+ onProgressFinalized();
}
};
@@ -74,6 +91,30 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
@NonNull PreviewSizeData extends Number> sizeData) {
super(context, preferenceKey);
mSizeData = sizeData;
+ mHandler = new Handler(context.getMainLooper());
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ // Restore the tooltip.
+ if (savedInstanceState != null
+ && savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
+ mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ // remove runnables in the queue.
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+ if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
+ outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
+ }
}
void setInteractionListener(ProgressInteractionListener interactionListener) {
@@ -91,11 +132,15 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
final int dataSize = mSizeData.getValues().size();
final int initialIndex = mSizeData.getInitialIndex();
+ mLastProgress = initialIndex;
mSeekBarPreference = screen.findPreference(getPreferenceKey());
mSeekBarPreference.setMax(dataSize - 1);
mSeekBarPreference.setProgress(initialIndex);
mSeekBarPreference.setContinuousUpdates(true);
mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
+ if (mNeedsQSTooltipReshow) {
+ mHandler.post(this::showQuickSettingsTooltipIfNeeded);
+ }
}
@Override
@@ -108,6 +153,44 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
mInteractionListener.ifPresent(ProgressInteractionListener::onProgressChanged);
}
+ private void onProgressFinalized() {
+ // Using progress in SeekBarPreference since the progresses in
+ // SeekBarPreference and seekbar are not always the same.
+ // See {@link androidx.preference.Preference#callChangeListener(Object)}
+ int seekBarPreferenceProgress = mSeekBarPreference.getProgress();
+ if (seekBarPreferenceProgress != mLastProgress) {
+ showQuickSettingsTooltipIfNeeded();
+ mLastProgress = seekBarPreferenceProgress;
+ }
+ }
+
+ private void showQuickSettingsTooltipIfNeeded() {
+ final ComponentName tileComponentName = getTileComponentName();
+ if (tileComponentName == null) {
+ // Returns if no tile service assigned.
+ return;
+ }
+
+ if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
+ mContext, tileComponentName)) {
+ // Returns if quick settings tooltip only show once.
+ return;
+ }
+
+ mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
+ mTooltipWindow.setup(getTileTooltipContent(),
+ R.drawable.accessibility_auto_added_qs_tooltip_illustration);
+ mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
+ AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
+ mNeedsQSTooltipReshow = false;
+ }
+
+ /** Returns the accessibility Quick Settings tile component name. */
+ abstract ComponentName getTileComponentName();
+
+ /** Returns accessibility Quick Settings tile tooltip content. */
+ abstract CharSequence getTileTooltipContent();
+
/**
* Interface for callbacks when users interact with the seek bar.
diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
index b35a5fe5a24..97a9071066e 100644
--- a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
@@ -16,11 +16,13 @@
package com.android.settings.accessibility;
+import static com.android.internal.accessibility.AccessibilityShortcutController.FONT_SIZE_COMPONENT_NAME;
import static com.android.settings.accessibility.TextReadingResetController.ResetStateListener;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -156,12 +158,34 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
controllers.add(mPreviewController);
final PreviewSizeSeekBarController fontSizeController = new PreviewSizeSeekBarController(
- context, FONT_SIZE_KEY, fontSizeData);
+ context, FONT_SIZE_KEY, fontSizeData) {
+ @Override
+ ComponentName getTileComponentName() {
+ return FONT_SIZE_COMPONENT_NAME;
+ }
+
+ @Override
+ CharSequence getTileTooltipContent() {
+ return context.getText(
+ R.string.accessibility_font_scaling_auto_added_qs_tooltip_content);
+ }
+ };
fontSizeController.setInteractionListener(mPreviewController);
+ getSettingsLifecycle().addObserver(fontSizeController);
controllers.add(fontSizeController);
final PreviewSizeSeekBarController displaySizeController = new PreviewSizeSeekBarController(
- context, DISPLAY_SIZE_KEY, displaySizeData);
+ context, DISPLAY_SIZE_KEY, displaySizeData) {
+ @Override
+ ComponentName getTileComponentName() {
+ return null;
+ }
+
+ @Override
+ CharSequence getTileTooltipContent() {
+ return null;
+ }
+ };
displaySizeController.setInteractionListener(mPreviewController);
controllers.add(displaySizeController);
diff --git a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java
index 1a7793e2ead..6144a73821c 100644
--- a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java
+++ b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java
@@ -213,7 +213,7 @@ public class AppLocaleDetails extends SettingsPreferenceFragment {
if (appLocale == null) {
return context.getString(R.string.preference_of_system_locale_summary);
} else {
- return LocaleHelper.getDisplayName(appLocale, appLocale, true);
+ return LocaleHelper.getDisplayName(appLocale.stripExtensions(), appLocale, true);
}
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index e2045f80c27..563c010d692 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -452,6 +452,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
item.registerCallback(this);
}
mProfileManager.addServiceListener(this);
+ refresh();
}
@Override
diff --git a/src/com/android/settings/display/ScreenResolutionFragment.java b/src/com/android/settings/display/ScreenResolutionFragment.java
index b40b48f246f..de7d25fefb9 100644
--- a/src/com/android/settings/display/ScreenResolutionFragment.java
+++ b/src/com/android/settings/display/ScreenResolutionFragment.java
@@ -251,10 +251,10 @@ public class ScreenResolutionFragment extends RadioButtonPickerFragment {
if (TextUtils.equals(
mScreenResolutionOptions[ScreenResolutionController.HIGHRESOLUTION_IDX], key)) {
- preference.setLottieAnimationResId(R.drawable.screen_resolution_1080p);
+ preference.setLottieAnimationResId(R.drawable.screen_resolution_high);
} else if (TextUtils.equals(
mScreenResolutionOptions[ScreenResolutionController.FULLRESOLUTION_IDX], key)) {
- preference.setLottieAnimationResId(R.drawable.screen_resolution_1440p);
+ preference.setLottieAnimationResId(R.drawable.screen_resolution_full);
}
}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index fac3aafe152..7713e270425 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -29,11 +29,12 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -71,7 +72,6 @@ import com.android.settings.core.CategoryMixin;
import com.android.settings.core.FeatureFlags;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.password.PasswordUtils;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settingslib.Utils;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
@@ -96,6 +96,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements
public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA =
"settings_large_screen_deep_link_intent_data";
+ // The referrer who fires the initial intent to start the homepage
+ @VisibleForTesting
+ static final String EXTRA_INITIAL_REFERRER = "initial_referrer";
+
static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network;
private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300;
@@ -177,12 +181,16 @@ public class SettingsHomepageActivity extends FragmentActivity implements
mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this);
if (mIsEmbeddingActivityEnabled) {
final UserManager um = getSystemService(UserManager.class);
- final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier());
+ final UserInfo userInfo = um.getUserInfo(getUserId());
if (userInfo.isManagedProfile()) {
final Intent intent = new Intent(getIntent())
- .setClass(this, DeepLinkHomepageActivityInternal.class)
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
- .putExtra(EXTRA_USER_HANDLE, getUser());
+ .putExtra(EXTRA_USER_HANDLE, getUser())
+ .putExtra(EXTRA_INITIAL_REFERRER, getCurrentReferrer());
+ if (TextUtils.equals(intent.getAction(), ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY)
+ && this instanceof DeepLinkHomepageActivity) {
+ intent.setClass(this, DeepLinkHomepageActivityInternal.class);
+ }
intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityAsUser(intent, um.getProfileParent(userInfo.id).getUserHandle());
finish();
@@ -471,7 +479,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
return;
}
- ActivityInfo targetActivityInfo = null;
+ ActivityInfo targetActivityInfo;
try {
targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName,
/* flags= */ 0);
@@ -481,23 +489,29 @@ public class SettingsHomepageActivity extends FragmentActivity implements
return;
}
- int callingUid = -1;
- try {
- callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken());
- } catch (RemoteException re) {
- Log.e(TAG, "Not able to get callingUid: " + re);
- finish();
- return;
+ UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class);
+ String caller = getInitialReferrer();
+ int callerUid = -1;
+ if (caller != null) {
+ try {
+ callerUid = getPackageManager().getApplicationInfoAsUser(caller,
+ ApplicationInfoFlags.of(/* flags= */ 0),
+ user != null ? user.getIdentifier() : getUserId()).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Not able to get callerUid: " + e);
+ finish();
+ return;
+ }
}
- if (!hasPrivilegedAccess(callingUid, targetActivityInfo)) {
+ if (!hasPrivilegedAccess(caller, callerUid, targetActivityInfo.packageName)) {
if (!targetActivityInfo.exported) {
Log.e(TAG, "Target Activity is not exported");
finish();
return;
}
- if (!isCallingAppPermitted(targetActivityInfo.permission)) {
+ if (!isCallingAppPermitted(targetActivityInfo.permission, callerUid)) {
Log.e(TAG, "Calling app must have the permission of deep link Activity");
finish();
return;
@@ -510,7 +524,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
& (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (targetIntent.getData() != null
&& uriPermissionFlags != 0
- && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callingUid,
+ && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callerUid,
uriPermissionFlags) == PackageManager.PERMISSION_DENIED) {
Log.e(TAG, "Calling app must have the permission to access Uri and grant permission");
finish();
@@ -547,7 +561,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements
SplitRule.FinishBehavior.ALWAYS,
true /* clearTop */);
- final UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class);
if (user != null) {
startActivityAsUser(targetIntent, user);
} else {
@@ -555,31 +568,30 @@ public class SettingsHomepageActivity extends FragmentActivity implements
}
}
- // Check if calling app has privileged access to launch Activity of activityInfo.
- private boolean hasPrivilegedAccess(int callingUid, ActivityInfo activityInfo) {
- if (TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()),
- getPackageName())) {
+ // Check if the caller has privileged access to launch the target page.
+ private boolean hasPrivilegedAccess(String callerPkg, int callerUid, String targetPackage) {
+ if (TextUtils.equals(callerPkg, getPackageName())) {
return true;
}
int targetUid = -1;
try {
- targetUid = getPackageManager().getApplicationInfo(activityInfo.packageName,
- /* flags= */ 0).uid;
- } catch (PackageManager.NameNotFoundException nnfe) {
- Log.e(TAG, "Not able to get targetUid: " + nnfe);
+ targetUid = getPackageManager().getApplicationInfo(targetPackage,
+ ApplicationInfoFlags.of(/* flags= */ 0)).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Not able to get targetUid: " + e);
return false;
}
// When activityInfo.exported is false, Activity still can be launched if applications have
// the same user ID.
- if (UserHandle.isSameApp(callingUid, targetUid)) {
+ if (UserHandle.isSameApp(callerUid, targetUid)) {
return true;
}
// When activityInfo.exported is false, Activity still can be launched if calling app has
// root or system privilege.
- int callingAppId = UserHandle.getAppId(callingUid);
+ int callingAppId = UserHandle.getAppId(callerUid);
if (callingAppId == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID) {
return true;
}
@@ -588,9 +600,31 @@ public class SettingsHomepageActivity extends FragmentActivity implements
}
@VisibleForTesting
- boolean isCallingAppPermitted(String permission) {
- return TextUtils.isEmpty(permission) || PasswordUtils.isCallingAppPermitted(
- this, getActivityToken(), permission);
+ String getInitialReferrer() {
+ String referrer = getCurrentReferrer();
+ if (!TextUtils.equals(referrer, getPackageName())) {
+ return referrer;
+ }
+
+ String initialReferrer = getIntent().getStringExtra(EXTRA_INITIAL_REFERRER);
+ return TextUtils.isEmpty(initialReferrer) ? referrer : initialReferrer;
+ }
+
+ @VisibleForTesting
+ String getCurrentReferrer() {
+ Intent intent = getIntent();
+ // Clear extras to get the real referrer
+ intent.removeExtra(Intent.EXTRA_REFERRER);
+ intent.removeExtra(Intent.EXTRA_REFERRER_NAME);
+ Uri referrer = getReferrer();
+ return referrer != null ? referrer.getHost() : null;
+ }
+
+ @VisibleForTesting
+ boolean isCallingAppPermitted(String permission, int callerUid) {
+ return TextUtils.isEmpty(permission)
+ || checkPermission(permission, /* pid= */ -1, callerUid)
+ == PackageManager.PERMISSION_GRANTED;
}
private String getHighlightMenuKey() {
diff --git a/src/com/android/settings/widget/LabeledSeekBarPreference.java b/src/com/android/settings/widget/LabeledSeekBarPreference.java
index 5d1011634c7..6300bd3b318 100644
--- a/src/com/android/settings/widget/LabeledSeekBarPreference.java
+++ b/src/com/android/settings/widget/LabeledSeekBarPreference.java
@@ -63,6 +63,8 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
private OnPreferenceChangeListener mStopListener;
private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener;
+ private SeekBar mSeekBar;
+
public LabeledSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
@@ -104,6 +106,10 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
com.android.internal.R.attr.seekBarPreferenceStyle), 0);
}
+ public SeekBar getSeekbar() {
+ return mSeekBar;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
@@ -133,19 +139,19 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
final boolean isValidTextResIdExist = mTextStartId > 0 || mTextEndId > 0;
labelFrame.setVisibility(isValidTextResIdExist ? View.VISIBLE : View.GONE);
- final SeekBar seekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar);
+ mSeekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar);
if (mTickMarkId != 0) {
final Drawable tickMark = getContext().getDrawable(mTickMarkId);
- seekBar.setTickMark(tickMark);
+ mSeekBar.setTickMark(tickMark);
}
final ViewGroup iconStartFrame = (ViewGroup) holder.findViewById(R.id.icon_start_frame);
final ImageView iconStartView = (ImageView) holder.findViewById(R.id.icon_start);
- updateIconStartIfNeeded(iconStartFrame, iconStartView, seekBar);
+ updateIconStartIfNeeded(iconStartFrame, iconStartView, mSeekBar);
final ViewGroup iconEndFrame = (ViewGroup) holder.findViewById(R.id.icon_end_frame);
final ImageView iconEndView = (ImageView) holder.findViewById(R.id.icon_end);
- updateIconEndIfNeeded(iconEndFrame, iconEndView, seekBar);
+ updateIconEndIfNeeded(iconEndFrame, iconEndView, mSeekBar);
}
public void setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 52ccb374ca6..6b0f5c00413 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -16,29 +16,45 @@
package com.android.settings.accessibility;
+import static com.android.internal.accessibility.AccessibilityShortcutController.FONT_SIZE_COMPONENT_NAME;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.PopupWindow;
import android.widget.SeekBar;
+import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.LabeledSeekBarPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowApplication;
/**
* Tests for {@link PreviewSizeSeekBarController}.
@@ -47,30 +63,67 @@ import org.robolectric.annotation.Config;
@Config(shadows = {ShadowInteractionJankMonitor.class})
public class PreviewSizeSeekBarControllerTest {
private static final String FONT_SIZE_KEY = "font_size";
+ private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
+ @Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
private PreviewSizeSeekBarController mSeekBarController;
private FontSizeData mFontSizeData;
private LabeledSeekBarPreference mSeekBarPreference;
- @Mock
private PreferenceScreen mPreferenceScreen;
+ private TestFragment mFragment;
+ private PreferenceViewHolder mHolder;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceManager mPreferenceManager;
@Mock
private PreviewSizeSeekBarController.ProgressInteractionListener mInteractionListener;
+ private static PopupWindow getLatestPopupWindow() {
+ final ShadowApplication shadowApplication =
+ Shadow.extract(ApplicationProvider.getApplicationContext());
+ return shadowApplication.getLatestPopupWindow();
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFontSizeData = new FontSizeData(mContext);
-
- mSeekBarController =
- new PreviewSizeSeekBarController(mContext, FONT_SIZE_KEY, mFontSizeData);
-
+ mContext.setTheme(R.style.Theme_AppCompat);
+ mFragment = spy(new TestFragment());
+ when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
+ when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mContext);
+ mPreferenceScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
+ when(mPreferenceScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
mSeekBarPreference = spy(new LabeledSeekBarPreference(mContext, /* attrs= */ null));
+ mSeekBarPreference.setKey(FONT_SIZE_KEY);
+
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
+ R.layout.preference_labeled_slider, null));
+ mSeekBarPreference.onBindViewHolder(mHolder);
+
when(mPreferenceScreen.findPreference(anyString())).thenReturn(mSeekBarPreference);
+ mFontSizeData = new FontSizeData(mContext);
+ mSeekBarController =
+ new PreviewSizeSeekBarController(mContext, FONT_SIZE_KEY, mFontSizeData) {
+ @Override
+ ComponentName getTileComponentName() {
+ return FONT_SIZE_COMPONENT_NAME;
+ }
+
+ @Override
+ CharSequence getTileTooltipContent() {
+ return mContext.getText(
+ R.string.accessibility_font_scaling_auto_added_qs_tooltip_content);
+ }
+ };
mSeekBarController.setInteractionListener(mInteractionListener);
+ when(mPreferenceScreen.findPreference(mSeekBarController.getPreferenceKey())).thenReturn(
+ mSeekBarPreference);
}
@Test
@@ -123,4 +176,64 @@ public class PreviewSizeSeekBarControllerTest {
verify(mInteractionListener).notifyPreferenceChanged();
}
+
+ @Test
+ public void onProgressChanged_showTooltipView() {
+ mSeekBarController.displayPreference(mPreferenceScreen);
+
+ // Simulate changing the progress for the first time
+ int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
+ mSeekBarPreference.setProgress(newProgress);
+ mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
+ newProgress,
+ /* fromUser= */ false);
+
+ assertThat(getLatestPopupWindow().isShowing()).isTrue();
+ }
+
+ @Test
+ public void onProgressChanged_tooltipViewHasBeenShown_notShowTooltipView() {
+ mSeekBarController.displayPreference(mPreferenceScreen);
+ // Simulate changing the progress for the first time
+ int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
+ mSeekBarPreference.setProgress(newProgress);
+ mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
+ newProgress,
+ /* fromUser= */ false);
+ getLatestPopupWindow().dismiss();
+
+ // Simulate progress changing for the second time
+ newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
+ mSeekBarPreference.setProgress(newProgress);
+ mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
+ newProgress,
+ /* fromUser= */ false);
+
+ assertThat(getLatestPopupWindow().isShowing()).isFalse();
+ }
+
+ @Test
+ @Config(shadows = ShadowFragment.class)
+ public void restoreValueFromSavedInstanceState_showTooltipView() {
+ final Bundle savedInstanceState = new Bundle();
+ savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
+ mSeekBarController.onCreate(savedInstanceState);
+
+ mSeekBarController.displayPreference(mPreferenceScreen);
+
+ assertThat(getLatestPopupWindow().isShowing()).isTrue();
+ }
+
+ private static class TestFragment extends SettingsPreferenceFragment {
+
+ @Override
+ protected boolean shouldSkipForInitialSUW() {
+ return false;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index 60f153cf233..f1d6796d775 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -20,8 +20,13 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -30,6 +35,8 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Build;
import android.view.View;
import android.view.Window;
@@ -215,29 +222,89 @@ public class SettingsHomepageActivityTest {
}
@Test
- @Config(shadows = {ShadowPasswordUtils.class})
+ public void getInitialReferrer_differentPackage_returnCurrentReferrer() {
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ String referrer = "com.abc";
+ doReturn(referrer).when(activity).getCurrentReferrer();
+
+ assertEquals(activity.getInitialReferrer(), referrer);
+ }
+
+ @Test
+ public void getInitialReferrer_noReferrerExtra_returnCurrentReferrer() {
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ String referrer = activity.getPackageName();
+ doReturn(referrer).when(activity).getCurrentReferrer();
+
+ assertEquals(activity.getInitialReferrer(), referrer);
+ }
+
+ @Test
+ public void getInitialReferrer_hasReferrerExtra_returnGivenReferrer() {
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ doReturn(activity.getPackageName()).when(activity).getCurrentReferrer();
+ String referrer = "com.abc";
+ activity.setIntent(new Intent().putExtra(SettingsHomepageActivity.EXTRA_INITIAL_REFERRER,
+ referrer));
+
+ assertEquals(activity.getInitialReferrer(), referrer);
+ }
+
+ @Test
+ public void getCurrentReferrer_hasReferrerExtra_shouldNotEqual() {
+ String referrer = "com.abc";
+ Uri uri = new Uri.Builder().scheme("android-app").authority(referrer).build();
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER, uri));
+
+ assertNotEquals(activity.getCurrentReferrer(), referrer);
+ }
+
+ @Test
+ public void getCurrentReferrer_hasReferrerNameExtra_shouldNotEqual() {
+ String referrer = "com.abc";
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER_NAME, referrer));
+
+ assertNotEquals(activity.getCurrentReferrer(), referrer);
+ }
+
+ @Test
public void isCallingAppPermitted_emptyPermission_returnTrue() {
- SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ doReturn(PackageManager.PERMISSION_DENIED).when(activity)
+ .checkPermission(anyString(), anyInt(), anyInt());
- assertTrue(homepageActivity.isCallingAppPermitted(""));
+ assertTrue(activity.isCallingAppPermitted("", 1000));
}
@Test
- @Config(shadows = {ShadowPasswordUtils.class})
- public void isCallingAppPermitted_noGrantedPermission_returnFalse() {
- SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+ public void isCallingAppPermitted_notGrantedPermission_returnFalse() {
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
+ doReturn(PackageManager.PERMISSION_DENIED).when(activity)
+ .checkPermission(anyString(), anyInt(), anyInt());
- assertFalse(homepageActivity.isCallingAppPermitted("android.permission.TEST"));
+ assertFalse(activity.isCallingAppPermitted("android.permission.TEST", 1000));
}
@Test
- @Config(shadows = {ShadowPasswordUtils.class})
public void isCallingAppPermitted_grantedPermission_returnTrue() {
- SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+ SettingsHomepageActivity activity =
+ spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
String permission = "android.permission.TEST";
- ShadowPasswordUtils.addGrantedPermission(permission);
+ doReturn(PackageManager.PERMISSION_DENIED).when(activity)
+ .checkPermission(anyString(), anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(activity)
+ .checkPermission(eq(permission), anyInt(), eq(1000));
- assertTrue(homepageActivity.isCallingAppPermitted(permission));
+ assertTrue(activity.isCallingAppPermitted(permission, 1000));
}
@Implements(SuggestionFeatureProviderImpl.class)