diff --git a/res/drawable/ic_accessibility_new.xml b/res/drawable/ic_accessibility_new.xml index 13472397895..265259cf587 100644 --- a/res/drawable/ic_accessibility_new.xml +++ b/res/drawable/ic_accessibility_new.xml @@ -15,8 +15,8 @@ --> diff --git a/res/drawable/ic_accessibility_page_indicator.xml b/res/drawable/ic_accessibility_page_indicator.xml new file mode 100644 index 00000000000..4fa8204c663 --- /dev/null +++ b/res/drawable/ic_accessibility_page_indicator.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/res/drawable/ic_apps.xml b/res/drawable/ic_apps.xml index 841c25a1e7b..8e9ba82eea0 100644 --- a/res/drawable/ic_apps.xml +++ b/res/drawable/ic_apps.xml @@ -25,5 +25,5 @@ 2zm4-8c0 1.1 .9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zm-4 2c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6 6c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 6c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" - android:fillColor="#FFFFFFFF"/> + android:fillColor="?android:attr/colorPrimary"/> diff --git a/res/drawable/ic_devices_other.xml b/res/drawable/ic_devices_other.xml index c7b6cf2ead2..5ef9f16dd5f 100644 --- a/res/drawable/ic_devices_other.xml +++ b/res/drawable/ic_devices_other.xml @@ -20,7 +20,7 @@ android:viewportHeight="24" android:viewportWidth="24"> diff --git a/res/drawable/ic_help.xml b/res/drawable/ic_help.xml index f8bac71e877..c0afa14307c 100644 --- a/res/drawable/ic_help.xml +++ b/res/drawable/ic_help.xml @@ -19,6 +19,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_phone_info.xml b/res/drawable/ic_phone_info.xml index 7aee3e86ebc..e0b64ee3c07 100644 --- a/res/drawable/ic_phone_info.xml +++ b/res/drawable/ic_phone_info.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_promote_conversation.xml b/res/drawable/ic_promote_conversation.xml index 39f7658b700..170f2baaaac 100644 --- a/res/drawable/ic_promote_conversation.xml +++ b/res/drawable/ic_promote_conversation.xml @@ -21,5 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,16L4,16L4,4h16v12zM6,12h8v2L6,14zM6,9h12v2L6,11zM6,6h12v2L6,8z"/> diff --git a/res/drawable/ic_settings_accessibility.xml b/res/drawable/ic_settings_accessibility.xml index d5c89b96fb3..0055a65d681 100644 --- a/res/drawable/ic_settings_accessibility.xml +++ b/res/drawable/ic_settings_accessibility.xml @@ -19,9 +19,9 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_accounts.xml b/res/drawable/ic_settings_accounts.xml index 7f182866d48..50dcf76a4bd 100644 --- a/res/drawable/ic_settings_accounts.xml +++ b/res/drawable/ic_settings_accounts.xml @@ -19,9 +19,9 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_battery_white.xml b/res/drawable/ic_settings_battery_white.xml index 255b75e7d9e..d1f91c5563c 100644 --- a/res/drawable/ic_settings_battery_white.xml +++ b/res/drawable/ic_settings_battery_white.xml @@ -19,6 +19,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_display_white.xml b/res/drawable/ic_settings_display_white.xml index 62dc21f305b..96be116f605 100644 --- a/res/drawable/ic_settings_display_white.xml +++ b/res/drawable/ic_settings_display_white.xml @@ -19,6 +19,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_location.xml b/res/drawable/ic_settings_location.xml index b7cfd6f2386..e1156a909cc 100644 --- a/res/drawable/ic_settings_location.xml +++ b/res/drawable/ic_settings_location.xml @@ -19,9 +19,9 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_privacy.xml b/res/drawable/ic_settings_privacy.xml index fcad83ab33b..cdb2ac63fa0 100644 --- a/res/drawable/ic_settings_privacy.xml +++ b/res/drawable/ic_settings_privacy.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/res/drawable/ic_settings_security_white.xml b/res/drawable/ic_settings_security_white.xml index b9c81f1ffdd..a90bb8f79f7 100644 --- a/res/drawable/ic_settings_security_white.xml +++ b/res/drawable/ic_settings_security_white.xml @@ -19,6 +19,6 @@ android:viewportWidth="24" android:viewportHeight="24"> \ No newline at end of file diff --git a/res/drawable/ic_settings_system_dashboard_white.xml b/res/drawable/ic_settings_system_dashboard_white.xml index 5a697d4ab21..cf3bb7b2330 100644 --- a/res/drawable/ic_settings_system_dashboard_white.xml +++ b/res/drawable/ic_settings_system_dashboard_white.xml @@ -19,12 +19,12 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_settings_wireless_white.xml b/res/drawable/ic_settings_wireless_white.xml index 63be43b290b..c56aa7e1b5c 100644 --- a/res/drawable/ic_settings_wireless_white.xml +++ b/res/drawable/ic_settings_wireless_white.xml @@ -16,4 +16,4 @@ \ No newline at end of file + android:tint="?android:attr/colorPrimary" /> \ No newline at end of file diff --git a/res/drawable/ic_storage_white.xml b/res/drawable/ic_storage_white.xml index eed91406ec1..3b5d3c32c7e 100644 --- a/res/drawable/ic_storage_white.xml +++ b/res/drawable/ic_storage_white.xml @@ -19,6 +19,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/drawable/ic_volume_up_24dp.xml b/res/drawable/ic_volume_up_24dp.xml index 8c68c0070f9..a1136d3ed08 100644 --- a/res/drawable/ic_volume_up_24dp.xml +++ b/res/drawable/ic_volume_up_24dp.xml @@ -20,12 +20,12 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/res/layout/accessibility_shortcut_tutorial_dialog.xml b/res/layout/accessibility_shortcut_tutorial_dialog.xml new file mode 100644 index 00000000000..9f3ee432178 --- /dev/null +++ b/res/layout/accessibility_shortcut_tutorial_dialog.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + diff --git a/res/layout/conversation_onboarding.xml b/res/layout/conversation_onboarding.xml new file mode 100644 index 00000000000..bc180947831 --- /dev/null +++ b/res/layout/conversation_onboarding.xml @@ -0,0 +1,43 @@ + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 5ab9c33915b..436c42ab0a3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -24,6 +24,8 @@ Allow Deny + + Turn on Unknown @@ -7266,19 +7268,33 @@ Use LTE services to improve voice and other communications (recommended) Use 4G services to improve voice and other communications (recommended) - - Contact discovery - - Allows your carrier to discover which calling features your contacts support. - + Send your contacts\u2019 phone numbers to provide enhanced features + + Send contacts to %1$s? + - Enable contact discovery? - - All of the phone numbers in your contacts will be periodically sent to your carrier in order to discover which calling features are supported. + Send contacts to your carrier? + + Your contacts\u2019 phone numbers will be periodically sent to %1$s.\n\nThis info identifies whether your contacts can use certain features, like video calls or some messaging features. + + Your contacts\u2019 phone numbers will be periodically sent to your carrier.\n\nThis info identifies whether your contacts can use certain features, like video calls or some messaging features. Preferred network type @@ -8367,14 +8383,31 @@ Priority conversations + + Show at top of conversation section and appear as floating bubbles + + + Show at top of conversation section + - Other conversations + Modified conversations + + + Conversations you\u2019ve made changes to - Bubble important conversations + Bubble priority conversations - Important conversations show at the top of the pull-down shade. You can also set them to bubble and interrupt Do Not Disturb. + Priority conversations show at the top of the pull-down shade. You can also set them to bubble and interrupt Do Not Disturb. + + + Priority and modified conversations will appear here + + + Once you mark a conversation as priority, or make any other changes to conversations, they will appear here. + \n\nTo change conversation settings: + \nSwipe down from the top of the screen to open the pull-down shade, then touch & hold a conversation. @@ -8446,7 +8479,7 @@ When device is unlocked, show notifications as a banner across the top of the screen - All %1$s notifications + All \"%1$s\" notifications Adaptive Notifications @@ -11905,18 +11938,27 @@ Show cards, passes, and device controls + + Lock screen + Don\u2019t show any content - Privacy + Sensitive content Show cards and controls when locked + + Show controls when locked + Hide cards and controls when locked + + To use, first set a screen lock + Show device controls diff --git a/res/xml/conversation_list_settings.xml b/res/xml/conversation_list_settings.xml index e18b4e57a42..d528bd7ac15 100644 --- a/res/xml/conversation_list_settings.xml +++ b/res/xml/conversation_list_settings.xml @@ -20,6 +20,12 @@ android:key="conversation_list" android:title="@string/zen_mode_conversations_title"> + + + + + + + diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java index ddf7bd442f8..58245b70912 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -16,35 +16,55 @@ package com.android.settings.accessibility; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; + import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ImageSpan; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.TextureView; import android.view.View; +import android.view.ViewGroup; import android.view.Window; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextSwitcher; import android.widget.TextView; +import androidx.annotation.AnimRes; import androidx.annotation.ColorInt; import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; +import androidx.core.util.Preconditions; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; import com.android.settings.R; +import com.android.settings.Utils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Utility class for creating the dialog that guides users for gesture navigation for * accessibility services. */ -public class AccessibilityGestureNavigationTutorial { - +public final class AccessibilityGestureNavigationTutorial { /** IntDef enum for dialog type. */ @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -59,6 +79,8 @@ public class AccessibilityGestureNavigationTutorial { int GESTURE_NAVIGATION_SETTINGS = 2; } + private AccessibilityGestureNavigationTutorial() {} + private static final DialogInterface.OnClickListener mOnClickListener = (DialogInterface dialog, int which) -> dialog.dismiss(); @@ -91,6 +113,13 @@ public class AccessibilityGestureNavigationTutorial { return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION); } + static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) { + return new AlertDialog.Builder(context) + .setView(createShortcutNavigationContentView(context, shortcutTypes)) + .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener) + .create(); + } + /** * Get a content View for a dialog to confirm that they want to enable a service. * @@ -201,4 +230,302 @@ public class AccessibilityGestureNavigationTutorial { typedArray.recycle(); return colorResId; } + + private static class TutorialPagerAdapter extends PagerAdapter { + private final List mTutorialPages; + private TutorialPagerAdapter(List tutorialPages) { + this.mTutorialPages = tutorialPages; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + final View itemView = mTutorialPages.get(position).getImageView(); + container.addView(itemView); + return itemView; + } + + @Override + public int getCount() { + return mTutorialPages.size(); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object o) { + return view == o; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, + @NonNull Object object) { + final View itemView = mTutorialPages.get(position).getImageView(); + container.removeView(itemView); + } + } + + private static ImageView createImageView(Context context, int imageRes) { + final ImageView imageView = new ImageView(context); + imageView.setImageResource(imageRes); + imageView.setAdjustViewBounds(true); + + return imageView; + } + + private static View createShortcutNavigationContentView(Context context, int shortcutTypes) { + final LayoutInflater inflater = context.getSystemService(LayoutInflater.class); + final View contentView = inflater.inflate( + R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null); + final List tutorialPages = + createShortcutTutorialPages(context, shortcutTypes); + Preconditions.checkArgument(!tutorialPages.isEmpty(), + /* errorMessage= */ "Unexpected tutorial pages size"); + + final LinearLayout indicatorContainer = contentView.findViewById(R.id.indicator_container); + indicatorContainer.setVisibility(tutorialPages.size() > 1 ? VISIBLE : GONE); + for (TutorialPage page : tutorialPages) { + indicatorContainer.addView(page.getIndicatorIcon()); + } + tutorialPages.get(/* firstIndex */ 0).getIndicatorIcon().setEnabled(true); + + final TextSwitcher title = contentView.findViewById(R.id.title); + title.setFactory(() -> makeTitleView(context)); + title.setText(tutorialPages.get(/* firstIndex */ 0).getTitle()); + + final TextSwitcher instruction = contentView.findViewById(R.id.instruction); + instruction.setFactory(() -> makeInstructionView(context)); + instruction.setText(tutorialPages.get(/* firstIndex */ 0).getInstruction()); + + final ViewPager viewPager = contentView.findViewById(R.id.view_pager); + viewPager.setAdapter(new TutorialPagerAdapter(tutorialPages)); + viewPager.addOnPageChangeListener( + new TutorialPageChangeListener(context, title, instruction, tutorialPages)); + + return contentView; + } + + private static View makeTitleView(Context context) { + final String familyName = + context.getString( + com.android.internal.R.string.config_headlineFontFamilyMedium); + final TextView textView = new TextView(context); + + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 20); + textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary)); + textView.setGravity(Gravity.CENTER); + textView.setTypeface(Typeface.create(familyName, Typeface.NORMAL)); + + return textView; + } + + private static View makeInstructionView(Context context) { + final TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 16); + textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary)); + textView.setTypeface( + Typeface.create(/* familyName= */ "sans-serif", Typeface.NORMAL)); + return textView; + } + + private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) { + final CharSequence title = getSoftwareTitle(context); + final ImageView image = createSoftwareImage(context); + final CharSequence instruction = getSoftwareInstruction(context); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + private static TutorialPage createHardwareTutorialPage(@NonNull Context context) { + final CharSequence title = + context.getText(R.string.accessibility_tutorial_dialog_title_volume); + final ImageView image = + createImageView(context, R.drawable.accessibility_shortcut_type_hardware); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + final CharSequence instruction = + context.getText(R.string.accessibility_tutorial_dialog_message_volume); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) { + final CharSequence title = + context.getText(R.string.accessibility_tutorial_dialog_title_triple); + final ImageView image = + createImageView(context, R.drawable.accessibility_shortcut_type_triple_tap); + final CharSequence instruction = + context.getText(R.string.accessibility_tutorial_dialog_message_triple); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + @VisibleForTesting + static List createShortcutTutorialPages(@NonNull Context context, + int shortcutTypes) { + final List tutorialPages = new ArrayList<>(); + if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { + tutorialPages.add(createSoftwareTutorialPage(context)); + } + + if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) { + tutorialPages.add(createHardwareTutorialPage(context)); + } + + if ((shortcutTypes & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) { + tutorialPages.add(createTripleTapTutorialPage(context)); + } + + return tutorialPages; + } + + private static CharSequence getSoftwareTitle(Context context) { + final boolean isGestureNavigationEnabled = + AccessibilityUtil.isGestureNavigateEnabled(context); + final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context); + + return (isGestureNavigationEnabled || isTouchExploreEnabled) + ? context.getText(R.string.accessibility_tutorial_dialog_title_gesture) + : context.getText(R.string.accessibility_tutorial_dialog_title_button); + } + + private static ImageView createSoftwareImage(Context context) { + int resId = R.drawable.accessibility_shortcut_type_software; + if (AccessibilityUtil.isGestureNavigateEnabled(context)) { + resId = AccessibilityUtil.isTouchExploreEnabled(context) + ? R.drawable.accessibility_shortcut_type_software_gesture_talkback + : R.drawable.accessibility_shortcut_type_software_gesture; + } + + return createImageView(context, resId); + } + + private static CharSequence getSoftwareInstruction(Context context) { + final boolean isGestureNavigateEnabled = + AccessibilityUtil.isGestureNavigateEnabled(context); + final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context); + int resId = R.string.accessibility_tutorial_dialog_message_button; + if (isGestureNavigateEnabled) { + resId = isTouchExploreEnabled + ? R.string.accessibility_tutorial_dialog_message_gesture_talkback + : R.string.accessibility_tutorial_dialog_message_gesture; + } + + CharSequence text = context.getText(resId); + if (resId == R.string.accessibility_tutorial_dialog_message_button) { + text = getSoftwareInstructionWithIcon(context, text); + } + + return text; + } + + private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) { + final String message = text.toString(); + final SpannableString spannableInstruction = SpannableString.valueOf(message); + final int indexIconStart = message.indexOf("%s"); + final int indexIconEnd = indexIconStart + 2; + final ImageView iconView = new ImageView(context); + iconView.setImageDrawable(context.getDrawable(R.drawable.ic_accessibility_new)); + final Drawable icon = iconView.getDrawable().mutate(); + final ImageSpan imageSpan = new ImageSpan(icon); + imageSpan.setContentDescription(""); + icon.setBounds(/* left= */ 0, /* top= */ 0, + icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); + spannableInstruction.setSpan(imageSpan, indexIconStart, indexIconEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + return spannableInstruction; + } + + private static class TutorialPage { + private final CharSequence mTitle; + private final ImageView mImageView; + private final ImageView mIndicatorIcon; + private final CharSequence mInstruction; + + TutorialPage(CharSequence title, ImageView imageView, ImageView indicatorIcon, + CharSequence instruction) { + this.mTitle = title; + this.mImageView = imageView; + this.mIndicatorIcon = indicatorIcon; + this.mInstruction = instruction; + } + + public CharSequence getTitle() { + return mTitle; + } + + public ImageView getImageView() { + return mImageView; + } + + public ImageView getIndicatorIcon() { + return mIndicatorIcon; + } + + public CharSequence getInstruction() { + return mInstruction; + } + } + + private static class TutorialPageChangeListener implements ViewPager.OnPageChangeListener { + private int mLastTutorialPagePosition = 0; + private final Context mContext; + private final TextSwitcher mTitle; + private final TextSwitcher mInstruction; + private final List mTutorialPages; + + TutorialPageChangeListener(Context context, ViewGroup title, ViewGroup instruction, + List tutorialPages) { + this.mContext = context; + this.mTitle = (TextSwitcher) title; + this.mInstruction = (TextSwitcher) instruction; + this.mTutorialPages = tutorialPages; + } + + @Override + public void onPageScrolled(int position, float positionOffset, + int positionOffsetPixels) { + // Do nothing. + } + + @Override + public void onPageSelected(int position) { + final boolean isPreviousPosition = + mLastTutorialPagePosition > position; + @AnimRes + final int inAnimationResId = isPreviousPosition + ? android.R.anim.slide_in_left + : com.android.internal.R.anim.slide_in_right; + + @AnimRes + final int outAnimationResId = isPreviousPosition + ? android.R.anim.slide_out_right + : com.android.internal.R.anim.slide_out_left; + + mTitle.setInAnimation(mContext, inAnimationResId); + mTitle.setOutAnimation(mContext, outAnimationResId); + mTitle.setText(mTutorialPages.get(position).getTitle()); + + mInstruction.setInAnimation(mContext, inAnimationResId); + mInstruction.setOutAnimation(mContext, outAnimationResId); + mInstruction.setText(mTutorialPages.get(position).getInstruction()); + + for (TutorialPage page : mTutorialPages) { + page.getIndicatorIcon().setEnabled(false); + } + mTutorialPages.get(position).getIndicatorIcon().setEnabled(true); + mLastTutorialPagePosition = position; + } + + @Override + public void onPageScrollStateChanged(int state) { + // Do nothing. + } + } } diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 3d47ca8dd8d..becd4e78b02 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -192,7 +192,7 @@ public class AccessibilitySettings extends DashboardFragment { } // Observe changes from accessibility selection menu - shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) { @Override diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java index ce0b5fb4b34..a0e3d81403e 100644 --- a/src/com/android/settings/accessibility/AccessibilityUtil.java +++ b/src/com/android/settings/accessibility/AccessibilityUtil.java @@ -342,7 +342,7 @@ final class AccessibilityUtil { static String convertKeyFromSettings(@UserShortcutType int shortcutType) { switch (shortcutType) { case UserShortcutType.SOFTWARE: - return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; case UserShortcutType.HARDWARE: return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; case UserShortcutType.TRIPLETAP: diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index de37babfc32..169a9521323 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -100,6 +100,12 @@ public class LaunchAccessibilityActivityPreferenceFragment extends showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Do not call super. We don't want to see the "Help & feedback" option on this page so as diff --git a/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java index 1803874f09c..4fcfff8e7f7 100644 --- a/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java @@ -18,6 +18,7 @@ package com.android.settings.accessibility; import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; +import android.accessibilityservice.AccessibilityServiceInfo; import android.os.Bundle; import android.view.View; @@ -48,6 +49,22 @@ public class LegacyAccessibilityServicePreferenceFragment extends setAllowedPreferredShortcutType(UserShortcutType.HARDWARE); } + @Override + int getUserShortcutTypes() { + int shortcutTypes = super.getUserShortcutTypes(); + + final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + final boolean hasRequestAccessibilityButtonFlag = + (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + if (hasRequestAccessibilityButtonFlag) { + shortcutTypes |= UserShortcutType.SOFTWARE; + } else { + shortcutTypes &= (~UserShortcutType.SOFTWARE); + } + + return shortcutTypes; + } + private void setAllowedPreferredShortcutType(int type) { final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType( mComponentName.flattenToString(), type); diff --git a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java index 0e6d3becc57..d589f0f03d4 100644 --- a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java @@ -120,7 +120,7 @@ public final class MagnificationPreferenceFragment extends DashboardFragment { final AccessibilityManager am = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final String assignedId = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); if (!TextUtils.isEmpty(assignedId) && !MAGNIFICATION_COMPONENT_ID.equals(assignedId)) { final ComponentName assignedComponentName = ComponentName.unflattenFromString( assignedId); diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index d530a563365..86181e9fe3b 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -106,7 +106,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends // capabilities. For // example, before JellyBean MR2 the user was granting the explore by touch // one. - private AccessibilityServiceInfo getAccessibilityServiceInfo() { + AccessibilityServiceInfo getAccessibilityServiceInfo() { final List infos = AccessibilityManager.getInstance( getPrefContext()).getInstalledAccessibilityServiceList(); @@ -164,16 +164,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends this::onDialogButtonFromDisableToggleClicked); break; } - case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: { - if (AccessibilityUtil.isGestureNavigateEnabled(getPrefContext())) { - mDialog = AccessibilityGestureNavigationTutorial - .showGestureNavigationTutorialDialog(getPrefContext()); - } else { - mDialog = AccessibilityGestureNavigationTutorial - .showAccessibilityButtonTutorialDialog(getPrefContext()); - } - break; - } default: { mDialog = super.onCreateDialog(dialogId); } @@ -197,6 +187,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends } } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + @Override protected void updateToggleServiceTitle(SwitchPreference switchPreference) { final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); @@ -301,6 +297,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends } else { AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } } else { AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, @@ -414,6 +411,9 @@ public class ToggleAccessibilityServicePreferenceFragment extends final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE); AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + mIsDialogShown.set(false); + showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); + mDialog.dismiss(); mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); diff --git a/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java b/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java index b98a503fe8b..47c1ca787d7 100644 --- a/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java +++ b/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java @@ -153,7 +153,7 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (KEY_DELAY_MODE.equals(key)) { - int delayMillis = getSharedPreferenceForDelayValue(); + final int delayMillis = getSharedPreferenceForDelayValue(); updateCustomDelayValue(delayMillis); } } @@ -172,10 +172,11 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro } private int getSharedPreferenceForDelayValue() { - int delayMillis = mSharedPreferences.getInt(KEY_CUSTOM_DELAY_VALUE, + final int delayMillis = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); - return delayMillis; + return mSharedPreferences.getInt(KEY_CUSTOM_DELAY_VALUE, delayMillis); } private void putSecureInt(String name, int value) { @@ -190,14 +191,14 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro } private void minusDelayByImageView() { - int delayMillis = getSharedPreferenceForDelayValue(); + final int delayMillis = getSharedPreferenceForDelayValue(); if (delayMillis > MIN_AUTOCLICK_DELAY_MS) { updateCustomDelayValue(delayMillis - AUTOCLICK_DELAY_STEP); } } private void plusDelayByImageView() { - int delayMillis = getSharedPreferenceForDelayValue(); + final int delayMillis = getSharedPreferenceForDelayValue(); if (delayMillis < MAX_AUTOCLICK_DELAY_MS) { updateCustomDelayValue(delayMillis + AUTOCLICK_DELAY_STEP); } diff --git a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java index 45cdf913fe0..b9af7ce3ee8 100644 --- a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java +++ b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java @@ -116,7 +116,7 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle @Override public void onRadioButtonClicked(RadioButtonPreference preference) { - int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey); + final int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey); handleRadioButtonPreferenceChange(value); if (mOnChangeListener != null) { mOnChangeListener.onCheckedChanged(mDelayModePref); @@ -137,11 +137,15 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle public void updateState(Preference preference) { super.updateState(preference); - mCurrentUiAutoClickMode = getSharedPreferenceForAutoClickMode(); + final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; + + mCurrentUiAutoClickMode = + enabled ? getSharedPreferenceForAutoClickMode() : AUTOCLICK_OFF_MODE; // Reset RadioButton. mDelayModePref.setChecked(false); - int mode = mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey()); + final int mode = mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey()); updatePreferenceCheckedState(mode); updatePreferenceVisibleState(mode); } @@ -155,10 +159,10 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle } private void setAutoclickModeToKeyMap() { - String[] autoclickKeys = mResources.getStringArray( + final String[] autoclickKeys = mResources.getStringArray( R.array.accessibility_autoclick_control_selector_keys); - int[] autoclickValues = mResources.getIntArray( + final int[] autoclickValues = mResources.getIntArray( R.array.accessibility_autoclick_selector_values); final int autoclickValueCount = autoclickValues.length; @@ -168,19 +172,14 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle } private void handleRadioButtonPreferenceChange(int preference) { - if (preference == AUTOCLICK_OFF_MODE) { - putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, /*value= */ 0); - } else { - putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, /*value= */ 1); - } + putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, + (preference != AUTOCLICK_OFF_MODE) ? /* enabled */ 1 : /* disabled */ 0); mSharedPreferences.edit().putInt(KEY_DELAY_MODE, preference).apply(); - if (preference == AUTOCLICK_CUSTOM_MODE) { - return; + if (preference != AUTOCLICK_CUSTOM_MODE) { + putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, preference); } - - putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, preference); } private void putSecureInt(String name, int value) { @@ -188,6 +187,6 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle } private int getSharedPreferenceForAutoClickMode() { - return mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_OFF_MODE); + return mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE); } } diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java index 07ff7203013..5cde5e602e7 100644 --- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java @@ -124,6 +124,12 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + private void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; if (mToggleServiceDividerSwitchPreference.isChecked() == checked) { diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java index 5b9c55b3a93..c7212a44b21 100644 --- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java @@ -198,6 +198,12 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + private void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; if (mToggleServiceDividerSwitchPreference.isChecked() == checked) { diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index eeb1f3ae5cf..8b4f3f25bd7 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -42,7 +42,6 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan import android.widget.CheckBox; import android.widget.ImageView; -import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; @@ -250,14 +249,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference @Override public Dialog onCreateDialog(int dialogId) { + Dialog dialog; switch (dialogId) { case DialogEnums.EDIT_SHORTCUT: final CharSequence dialogTitle = getPrefContext().getString( R.string.accessibility_shortcut_title, mPackageName); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked); initializeDialogCheckBox(dialog); return dialog; + case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + dialog = AccessibilityGestureNavigationTutorial + .createAccessibilityTutorialDialog(getPrefContext(), + getUserShortcutTypes()); + dialog.setCanceledOnTouchOutside(false); + return dialog; default: throw new IllegalArgumentException("Unsupported dialogId " + dialogId); } @@ -268,6 +274,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference switch (dialogId) { case DialogEnums.EDIT_SHORTCUT: return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT; + case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL; default: return SettingsEnums.ACTION_UNKNOWN; } @@ -334,6 +342,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference removeActionBarToggleSwitch(); } + /** + * Returns the shortcut type list which has been checked by user. + */ + abstract int getUserShortcutTypes(); + protected void updateToggleServiceTitle(SwitchPreference switchPreference) { switchPreference.setTitle(R.string.accessibility_service_master_switch_title); } @@ -658,6 +671,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference if (preference.isChecked()) { AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } else { AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, mComponentName); diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 91b8eaebd47..b550434fc8e 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -224,24 +224,22 @@ public class ToggleScreenMagnificationPreferenceFragment extends @Override public Dialog onCreateDialog(int dialogId) { + final AlertDialog dialog; switch (dialogId) { case DialogEnums.GESTURE_NAVIGATION_TUTORIAL: return AccessibilityGestureNavigationTutorial .showGestureNavigationTutorialDialog(getPrefContext()); - case DialogEnums.ACCESSIBILITY_BUTTON_TUTORIAL: - return AccessibilityGestureNavigationTutorial - .showAccessibilityButtonTutorialDialog(getPrefContext()); case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: final CharSequence dialogTitle = getPrefContext().getString( R.string.accessibility_shortcut_title, mPackageName); - final AlertDialog dialog = - AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog( + dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog( getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked); initializeDialogCheckBox(dialog); return dialog; + default: + return super.onCreateDialog(dialogId); } - throw new IllegalArgumentException("Unsupported dialogId " + dialogId); } private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) { @@ -408,18 +406,21 @@ public class ToggleScreenMagnificationPreferenceFragment extends case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: return SettingsEnums.DIALOG_MAGNIFICATION_EDIT_SHORTCUT; default: - return 0; + return super.getDialogMetricsCategory(dialogId); } } + @Override + int getUserShortcutTypes() { + return getUserShortcutTypeFromSettings(getPrefContext()); + } + @Override protected void onPreferenceToggled(String preferenceKey, boolean enabled) { if (enabled && TextUtils.equals( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, preferenceKey)) { - showDialog(AccessibilityUtil.isGestureNavigateEnabled(getPrefContext()) - ? DialogEnums.GESTURE_NAVIGATION_TUTORIAL - : DialogEnums.ACCESSIBILITY_BUTTON_TUTORIAL); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } MagnificationPreferenceFragment.setChecked(getContentResolver(), preferenceKey, enabled); } @@ -449,6 +450,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE); if (preference.isChecked()) { optInAllMagnificationValuesToSettings(getPrefContext(), shortcutTypes); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } else { optOutAllMagnificationValuesFromSettings(getPrefContext(), shortcutTypes); } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index e4041fbac55..8084038ba68 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -320,6 +320,14 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment addPreferencesFromResource(resId); final PreferenceScreen screen = getPreferenceScreen(); screen.setOnExpandButtonClickListener(this); + displayResourceTilesToScreen(screen); + } + + /** + * Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)} + * on all {@link AbstractPreferenceController}s. + */ + protected void displayResourceTilesToScreen(PreferenceScreen screen) { mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach( controller -> controller.displayPreference(screen)); } diff --git a/src/com/android/settings/datausage/CellDataPreference.java b/src/com/android/settings/datausage/CellDataPreference.java index 4a49f333cbb..6dc40e86aa9 100644 --- a/src/com/android/settings/datausage/CellDataPreference.java +++ b/src/com/android/settings/datausage/CellDataPreference.java @@ -50,15 +50,12 @@ public class CellDataPreference extends CustomDialogPreferenceCompat public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; public boolean mChecked; public boolean mMultiSimDialog; - @VisibleForTesting - ProxySubscriptionManager mProxySubscriptionMgr; private MobileDataEnabledListener mDataStateListener; public CellDataPreference(Context context, AttributeSet attrs) { super(context, attrs, TypedArrayUtils.getAttr(context, androidx.preference.R.attr.switchPreferenceStyle, android.R.attr.switchPreferenceStyle)); - mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context); mDataStateListener = new MobileDataEnabledListener(context, this); } @@ -88,13 +85,15 @@ public class CellDataPreference extends CustomDialogPreferenceCompat public void onAttached() { super.onAttached(); mDataStateListener.start(mSubId); - mProxySubscriptionMgr.addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); + getProxySubscriptionManager() + .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); } @Override public void onDetached() { mDataStateListener.stop(); - mProxySubscriptionMgr.removeActiveSubscriptionsListener(mOnSubscriptionsChangeListener); + getProxySubscriptionManager() + .removeActiveSubscriptionsListener(mOnSubscriptionsChangeListener); super.onDetached(); } @@ -104,8 +103,8 @@ public class CellDataPreference extends CustomDialogPreferenceCompat throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo"); } - mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext()); - mProxySubscriptionMgr.addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); + getProxySubscriptionManager() + .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { mSubId = subId; @@ -115,6 +114,16 @@ public class CellDataPreference extends CustomDialogPreferenceCompat updateChecked(); } + @VisibleForTesting + ProxySubscriptionManager getProxySubscriptionManager() { + return ProxySubscriptionManager.getInstance(getContext()); + } + + @VisibleForTesting + SubscriptionInfo getActiveSubscriptionInfo(int subId) { + return getProxySubscriptionManager().getActiveSubscriptionInfo(subId); + } + private void updateChecked() { setChecked(getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId)); } @@ -122,7 +131,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat private void updateEnabled() { // If this subscription is not active, for example, SIM card is taken out, we disable // the button. - setEnabled(mProxySubscriptionMgr.getActiveSubscriptionInfo(mSubId) != null); + setEnabled(getActiveSubscriptionInfo(mSubId) != null); } @Override @@ -130,9 +139,8 @@ public class CellDataPreference extends CustomDialogPreferenceCompat final Context context = getContext(); FeatureFactory.getFactory(context).getMetricsFeatureProvider() .action(context, SettingsEnums.ACTION_CELL_DATA_TOGGLE, !mChecked); - final SubscriptionInfo currentSir = mProxySubscriptionMgr.getActiveSubscriptionInfo( - mSubId); - final SubscriptionInfo nextSir = mProxySubscriptionMgr.getActiveSubscriptionInfo( + final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); + final SubscriptionInfo nextSir = getActiveSubscriptionInfo( SubscriptionManager.getDefaultDataSubscriptionId()); if (mChecked) { setMobileDataEnabled(false); @@ -186,9 +194,8 @@ public class CellDataPreference extends CustomDialogPreferenceCompat private void showMultiSimDialog(Builder builder, DialogInterface.OnClickListener listener) { - final SubscriptionInfo currentSir = mProxySubscriptionMgr.getActiveSubscriptionInfo( - mSubId); - final SubscriptionInfo nextSir = mProxySubscriptionMgr.getActiveSubscriptionInfo( + final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); + final SubscriptionInfo nextSir = getActiveSubscriptionInfo( SubscriptionManager.getDefaultDataSubscriptionId()); final String previousName = (nextSir == null) @@ -205,8 +212,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat } private void disableDataForOtherSubscriptions(int subId) { - final SubscriptionInfo subInfo = mProxySubscriptionMgr.getActiveSubscriptionInfo( - subId); + final SubscriptionInfo subInfo = getActiveSubscriptionInfo(subId); if (subInfo != null) { getContext().getSystemService(TelephonyManager.class).setDataEnabled(subId, false); } @@ -218,7 +224,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat return; } if (mMultiSimDialog) { - mProxySubscriptionMgr.get().setDefaultDataSubId(mSubId); + getProxySubscriptionManager().get().setDefaultDataSubId(mSubId); setMobileDataEnabled(true); disableDataForOtherSubscriptions(mSubId); } else { diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java index 1a838007fec..2f2a04954ba 100644 --- a/src/com/android/settings/datausage/DataUsageSummary.java +++ b/src/com/android/settings/datausage/DataUsageSummary.java @@ -78,10 +78,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage super.onCreate(icicle); Context context = getContext(); - // Enable ProxySubscriptionMgr with Lifecycle support for all controllers - // live within this fragment - mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context); - mProxySubscriptionMgr.setLifecycle(getLifecycle()); + enableProxySubscriptionManager(context); boolean hasMobileData = DataUsageUtils.hasMobileData(context); @@ -151,6 +148,14 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage addMobileSection(subId, null); } + @VisibleForTesting + void enableProxySubscriptionManager(Context context) { + // Enable ProxySubscriptionMgr with Lifecycle support for all controllers + // live within this fragment + mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context); + mProxySubscriptionMgr.setLifecycle(getLifecycle()); + } + private void addMobileSection(int subId, SubscriptionInfo subInfo) { TemplatePreferenceCategory category = (TemplatePreferenceCategory) inflatePreferences(R.xml.data_usage_cellular); diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java index c0ee0a55dae..648b38c7e87 100644 --- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java +++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java @@ -64,7 +64,7 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f; // (1/0.8)^2 private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE; // 0.8^2 - private final EntityHeaderController mEntityHeaderController; + private EntityHeaderController mEntityHeaderController; private final Lifecycle mLifecycle; private final PreferenceFragmentCompat mFragment; protected DataUsageController mDataUsageController; @@ -103,8 +103,6 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc Lifecycle lifecycle, PreferenceFragmentCompat fragment, int subscriptionId) { super(activity, KEY); - mEntityHeaderController = EntityHeaderController.newInstance(activity, - fragment, null); mLifecycle = lifecycle; mFragment = fragment; init(subscriptionId); @@ -122,14 +120,14 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc mContext.getSystemService(NetworkPolicyManager.class); mPolicyEditor = new NetworkPolicyEditor(policyManager); - mHasMobileData = SubscriptionManager.isValidSubscriptionId(mSubId) - && DataUsageUtils.hasMobileData(mContext); + mHasMobileData = DataUsageUtils.hasMobileData(mContext); mDataUsageController = new DataUsageController(mContext); mDataUsageController.setSubscriptionId(mSubId); mDataInfoController = new DataUsageInfoController(); - if (mHasMobileData) { + final SubscriptionInfo subInfo = getSubscriptionInfo(mSubId); + if (subInfo != null) { mDataUsageTemplate = R.string.cell_data_template; } else if (DataUsageUtils.hasWifiRadio(mContext)) { mDataUsageTemplate = R.string.wifi_data_template; @@ -165,6 +163,10 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc @Override public void onStart() { + if (mEntityHeaderController == null) { + mEntityHeaderController = + EntityHeaderController.newInstance((Activity) mContext, mFragment, null); + } RecyclerView view = mFragment.getListView(); mEntityHeaderController.setRecyclerView(view, mLifecycle); mEntityHeaderController.styleActionBar((Activity) mContext); @@ -178,18 +180,16 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc @VisibleForTesting SubscriptionInfo getSubscriptionInfo(int subscriptionId) { + if (!mHasMobileData) { + return null; + } return ProxySubscriptionManager.getInstance(mContext) .getAccessibleSubscriptionInfo(subscriptionId); } - @VisibleForTesting - boolean hasSim() { - return DataUsageUtils.hasSim(mContext); - } - @Override public int getAvailabilityStatus(int subId) { - return hasSim() + return (getSubscriptionInfo(subId) != null) || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @@ -197,16 +197,19 @@ public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenc public void updateState(Preference preference) { DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference; - final DataUsageController.DataUsageInfo info; final SubscriptionInfo subInfo = getSubscriptionInfo(mSubId); - if (hasSim()) { - info = mDataUsageController.getDataUsageInfo(mDefaultTemplate); + if (subInfo == null) { + mDefaultTemplate = NetworkTemplate.buildTemplateWifiWildcard(); + } + + final DataUsageController.DataUsageInfo info = + mDataUsageController.getDataUsageInfo(mDefaultTemplate); + + if (subInfo != null) { mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate)); summaryPreference.setWifiMode(/* isWifiMode */ false, /* usagePeriod */ null, /* isSingleWifi */ false); } else { - info = mDataUsageController.getDataUsageInfo( - NetworkTemplate.buildTemplateWifiWildcard()); summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */ info.period, /* isSingleWifi */ false); summaryPreference.setLimitInfo(null); diff --git a/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java b/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java new file mode 100644 index 00000000000..29c6176a4f8 --- /dev/null +++ b/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 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.gestures; + +import android.content.ContentResolver; +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.overlay.FeatureFactory; + +public class PowerMenuPrivacyPreferenceController extends TogglePreferenceController { + + private static final String SETTING_KEY = Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT; + private static final String CARDS_AVAILABLE_KEY = + Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE; + private static final String CARDS_ENABLED_KEY = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED; + private static final String CONTROLS_ENABLED_KEY = Settings.Secure.CONTROLS_ENABLED; + + + public PowerMenuPrivacyPreferenceController(Context context, + String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public boolean isChecked() { + return Settings.Secure.getInt(mContext.getContentResolver(), SETTING_KEY, 0) != 0; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Secure.putInt(mContext.getContentResolver(), SETTING_KEY, + isChecked ? 1 : 0); + } + + @Override + public CharSequence getSummary() { + boolean cardsAvailable = Settings.Secure.getInt(mContext.getContentResolver(), + CARDS_AVAILABLE_KEY, 0) != 0; + final int res; + if (!isSecure()) { + res = R.string.power_menu_privacy_not_secure; + } else if (cardsAvailable) { + res = R.string.power_menu_privacy_show; + } else { + res = R.string.power_menu_privacy_show_controls; + } + return mContext.getText(res); + } + + @Override + public int getAvailabilityStatus() { + // hide if lockscreen isn't secure for this user + + return isEnabled() && isSecure() ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + preference.setEnabled(getAvailabilityStatus() != DISABLED_DEPENDENT_SETTING); + refreshSummary(preference); + } + + private boolean isEnabled() { + final ContentResolver resolver = mContext.getContentResolver(); + boolean cardsAvailable = Settings.Secure.getInt(resolver, CARDS_AVAILABLE_KEY, 0) != 0; + boolean cardsEnabled = Settings.Secure.getInt(resolver, CARDS_ENABLED_KEY, 0) != 0; + boolean controlsEnabled = Settings.Secure.getInt(resolver, CONTROLS_ENABLED_KEY, 0) != 0; + return (cardsAvailable && cardsEnabled) || controlsEnabled; + } + + private boolean isSecure() { + final LockPatternUtils utils = FeatureFactory.getFactory(mContext) + .getSecurityFeatureProvider() + .getLockPatternUtils(mContext); + int userId = UserHandle.myUserId(); + return utils.isSecure(userId); + } +} diff --git a/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java b/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java index 72730101d6d..26398d7e03c 100644 --- a/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java +++ b/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java @@ -23,6 +23,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.telephony.ims.ImsManager; import android.telephony.ims.ImsRcsManager; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentManager; @@ -39,9 +40,11 @@ public class ContactDiscoveryDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { private static final String SUB_ID_KEY = "sub_id_key"; + private static final String CARRIER_NAME_KEY = "carrier_name_key"; private static final String DIALOG_TAG = "discovery_dialog:"; private int mSubId; + private CharSequence mCarrierName; private ImsManager mImsManager; /** @@ -50,10 +53,11 @@ public class ContactDiscoveryDialogFragment extends InstrumentedDialogFragment * @param subId The subscription ID to associate with this Dialog. * @return a new instance of ContactDiscoveryDialogFragment. */ - public static ContactDiscoveryDialogFragment newInstance(int subId) { + public static ContactDiscoveryDialogFragment newInstance(int subId, CharSequence carrierName) { final ContactDiscoveryDialogFragment dialogFragment = new ContactDiscoveryDialogFragment(); final Bundle args = new Bundle(); args.putInt(SUB_ID_KEY, subId); + args.putCharSequence(CARRIER_NAME_KEY, carrierName); dialogFragment.setArguments(args); return dialogFragment; @@ -64,18 +68,30 @@ public class ContactDiscoveryDialogFragment extends InstrumentedDialogFragment super.onAttach(context); final Bundle args = getArguments(); mSubId = args.getInt(SUB_ID_KEY); + mCarrierName = args.getCharSequence(CARRIER_NAME_KEY); mImsManager = getImsManager(context); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - final int title = R.string.contact_discovery_opt_in_dialog_title; - int message = R.string.contact_discovery_opt_in_dialog_message; - builder.setMessage(getResources().getString(message)) + CharSequence title; + CharSequence message; + if (!TextUtils.isEmpty(mCarrierName)) { + title = getContext().getString( + R.string.contact_discovery_opt_in_dialog_title, mCarrierName); + message = getContext().getString( + R.string.contact_discovery_opt_in_dialog_message, mCarrierName); + } else { + title = getContext().getString( + R.string.contact_discovery_opt_in_dialog_title_no_carrier_defined); + message = getContext().getString( + R.string.contact_discovery_opt_in_dialog_message_no_carrier_defined); + } + builder.setMessage(message) .setTitle(title) .setIconAttribute(android.R.attr.alertDialogIcon) - .setPositiveButton(android.R.string.ok, this) + .setPositiveButton(R.string.confirmation_turn_on, this) .setNegativeButton(android.R.string.cancel, this); return builder.create(); } diff --git a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java index 21d49d9b2b9..880c444ed21 100644 --- a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java +++ b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java @@ -22,6 +22,7 @@ import android.net.Uri; import android.os.PersistableBundle; import android.provider.Telephony; import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; import android.telephony.ims.ImsManager; import android.util.Log; @@ -34,6 +35,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; +import com.android.settings.network.SubscriptionUtil; + /** * Controller for the "Contact Discovery" option present in MobileNetworkSettings. @@ -130,7 +133,19 @@ public class ContactDiscoveryPreferenceController extends TelephonyTogglePrefere private void showContentDiscoveryDialog() { ContactDiscoveryDialogFragment dialog = ContactDiscoveryDialogFragment.newInstance( - mSubId); + mSubId, getCarrierDisplayName(preference.getContext())); dialog.show(mFragmentManager, ContactDiscoveryDialogFragment.getFragmentTag(mSubId)); } + + private CharSequence getCarrierDisplayName(Context context) { + CharSequence result = ""; + + for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(context)) { + if (mSubId == info.getSubscriptionId()) { + result = info.getDisplayName(); + break; + } + } + return result; + } } diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java index 4499881ca76..035a8c16737 100644 --- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java +++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java @@ -22,22 +22,33 @@ import android.net.NetworkTemplate; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.text.TextUtils; +import android.util.Log; import androidx.preference.Preference; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; import com.android.settingslib.net.DataUsageController; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; /** * Preference controller for "Data usage" */ public class DataUsagePreferenceController extends TelephonyBasePreferenceController { - private NetworkTemplate mTemplate; + private static final String LOG_TAG = "DataUsagePreferCtrl"; + + private Future mTemplateFuture; + private AtomicReference mTemplate; public DataUsagePreferenceController(Context context, String key) { super(context, key); + mTemplate = new AtomicReference(); } @Override @@ -53,7 +64,7 @@ public class DataUsagePreferenceController extends TelephonyBasePreferenceContro return false; } final Intent intent = new Intent(Settings.ACTION_MOBILE_DATA_USAGE); - intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mTemplate); + intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, getNetworkTemplate()); intent.putExtra(Settings.EXTRA_SUB_ID, mSubId); mContext.startActivity(intent); @@ -78,22 +89,49 @@ public class DataUsagePreferenceController extends TelephonyBasePreferenceContro public void init(int subId) { mSubId = subId; + mTemplate.set(null); + mTemplateFuture = ThreadUtils.postOnBackgroundThread(() + -> fetchMobileTemplate(mContext, mSubId)); + } + private NetworkTemplate fetchMobileTemplate(Context context, int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { - return; + return null; } - mTemplate = DataUsageUtils.getDefaultTemplate(mContext, mSubId); + return DataUsageUtils.getMobileTemplate(context, subId); + } + + private NetworkTemplate getNetworkTemplate() { + if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { + return null; + } + NetworkTemplate template = mTemplate.get(); + if (template != null) { + return template; + } + try { + template = mTemplateFuture.get(); + mTemplate.set(template); + } catch (ExecutionException | InterruptedException | NullPointerException exception) { + Log.e(LOG_TAG, "Fail to get data usage template", exception); + } + return template; + } + + @VisibleForTesting + DataUsageController.DataUsageInfo getDataUsageInfo(DataUsageController controller) { + return controller.getDataUsageInfo(getNetworkTemplate()); } private CharSequence getDataUsageSummary(Context context, int subId) { final DataUsageController controller = new DataUsageController(context); controller.setSubscriptionId(subId); - final DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate); + final DataUsageController.DataUsageInfo usageInfo = getDataUsageInfo(controller); long usageLevel = usageInfo.usageLevel; if (usageLevel <= 0L) { - usageLevel = controller.getHistoricalUsageLevel(mTemplate); + usageLevel = controller.getHistoricalUsageLevel(getNetworkTemplate()); } if (usageLevel <= 0L) { return null; diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java index 5a4c70d7db9..fd7ab97cf85 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java +++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.os.UserManager; import android.provider.Settings; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.ims.ImsRcsManager; import android.text.TextUtils; import android.util.Log; @@ -79,7 +80,8 @@ public class MobileNetworkActivity extends SettingsBaseActivity int oldSubId = mCurSubscriptionId; mCurSubscriptionId = updateSubscriptionIndex; mFragmentForceReload = (mCurSubscriptionId == oldSubId); - updateSubscriptions(getSubscription()); + final SubscriptionInfo info = getSubscription(); + updateSubscriptions(info); // If the subscription has changed or the new intent doesnt contain the opt in action, // remove the old discovery dialog. If the activity is being recreated, we will see @@ -91,7 +93,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity // evaluate showing the new discovery dialog if this intent contains an action to show the // opt-in. if (doesIntentContainOptInAction(intent)) { - maybeShowContactDiscoveryDialog(updateSubscriptionIndex); + maybeShowContactDiscoveryDialog(info); } } @@ -125,7 +127,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity final SubscriptionInfo subscription = getSubscription(); updateTitleAndNavigation(subscription); - maybeShowContactDiscoveryDialog(mCurSubscriptionId); + maybeShowContactDiscoveryDialog(subscription); // Since onChanged() will take place immediately when addActiveSubscriptionsListener(), // perform registration after mCurSubscriptionId been configured. @@ -276,7 +278,13 @@ public class MobileNetworkActivity extends SettingsBaseActivity .findFragmentByTag(ContactDiscoveryDialogFragment.getFragmentTag(subId)); } - private void maybeShowContactDiscoveryDialog(int subId) { + private void maybeShowContactDiscoveryDialog(SubscriptionInfo info) { + int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + CharSequence carrierName = ""; + if (info != null) { + subId = info.getSubscriptionId(); + carrierName = info.getDisplayName(); + } // If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the // associated dialog only if the opt-in has not been granted yet. boolean showOptInDialog = doesIntentContainOptInAction(getIntent()) @@ -287,7 +295,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId); if (showOptInDialog) { if (fragment == null) { - fragment = ContactDiscoveryDialogFragment.newInstance(subId); + fragment = ContactDiscoveryDialogFragment.newInstance(subId, carrierName); } // Only try to show the dialog if it has not already been added, otherwise we may // accidentally add it multiple times, causing multiple dialogs. diff --git a/src/com/android/settings/notification/app/ConversationListSettings.java b/src/com/android/settings/notification/app/ConversationListSettings.java index 1eb997a0db9..8f232cae306 100644 --- a/src/com/android/settings/notification/app/ConversationListSettings.java +++ b/src/com/android/settings/notification/app/ConversationListSettings.java @@ -53,6 +53,7 @@ public class ConversationListSettings extends DashboardFragment { @Override protected List createPreferenceControllers(Context context) { mControllers = new ArrayList<>(); + mControllers.add(new NoConversationsPreferenceController(context, mBackend)); mControllers.add(new PriorityConversationsPreferenceController(context, mBackend)); mControllers.add(new AllConversationsPreferenceController(context, mBackend)); return new ArrayList<>(mControllers); diff --git a/src/com/android/settings/notification/app/NoConversationsPreferenceController.java b/src/com/android/settings/notification/app/NoConversationsPreferenceController.java new file mode 100644 index 00000000000..c5f0cb2c3dd --- /dev/null +++ b/src/com/android/settings/notification/app/NoConversationsPreferenceController.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 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.notification.app; + +import android.content.Context; +import android.os.AsyncTask; +import android.service.notification.ConversationChannelWrapper; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; + +import com.android.settings.notification.NotificationBackend; + +import java.util.Collections; +import java.util.List; + +public class NoConversationsPreferenceController extends ConversationListPreferenceController { + + private static final String KEY = "no_conversations"; + + private List mConversations; + + public NoConversationsPreferenceController(Context context, + NotificationBackend backend) { + super(context, backend); + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + boolean matchesFilter(ConversationChannelWrapper conversation) { + return false; + } + + @Override + public void updateState(Preference preference) { + // Load conversations + new AsyncTask() { + @Override + protected Void doInBackground(Void... unused) { + mConversations = mBackend.getConversations(false).getList(); + return null; + } + + @Override + protected void onPostExecute(Void unused) { + if (mContext == null) { + return; + } + preference.setVisible(mConversations.size() == 0); + } + }.execute(); + } +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java index b888daab5fd..005b4bb795f 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -30,6 +30,8 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -71,6 +73,7 @@ public class NotificationHistoryActivity extends Activity { private HistoryLoader mHistoryLoader; private INotificationManager mNm; + private UserManager mUm; private PackageManager mPm; private CountDownLatch mCountdownLatch; private Future mCountdownFuture; @@ -104,6 +107,8 @@ public class NotificationHistoryActivity extends Activity { TextView label = viewForPackage.findViewById(R.id.label); label.setText(nhp.label != null ? nhp.label : nhp.pkgName); + label.setContentDescription(mUm.getBadgedLabelForUser(label.getText(), + UserHandle.getUserHandleForUid(nhp.uid))); ImageView icon = viewForPackage.findViewById(R.id.icon); icon.setImageDrawable(nhp.icon); @@ -140,6 +145,7 @@ public class NotificationHistoryActivity extends Activity { super.onResume(); mPm = getPackageManager(); + mUm = getSystemService(UserManager.class); // wait for history loading and recent/snooze loading mCountdownLatch = new CountDownLatch(2); diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java new file mode 100644 index 00000000000..cf0ce96bf60 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 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; + +import static com.android.settings.accessibility.AccessibilityGestureNavigationTutorial.createAccessibilityTutorialDialog; +import static com.android.settings.accessibility.AccessibilityGestureNavigationTutorial.createShortcutTutorialPages; +import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import androidx.appcompat.app.AlertDialog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** Tests for {@link AccessibilityGestureNavigationTutorial}. */ +@RunWith(RobolectricTestRunner.class) +public final class AccessibilityGestureNavigationTutorialTest { + + private Context mContext; + private int mShortcutTypes; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mShortcutTypes = /* initial */ 0; + } + + @Test(expected = IllegalArgumentException.class) + public void createTutorialPages_shortcutListIsEmpty_throwsException() { + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + } + + @Test + public void createTutorialPages_turnOnTripleTapShortcut_hasOnePage() { + mShortcutTypes |= UserShortcutType.TRIPLETAP; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 1); + assertThat(alertDialog).isNotNull(); + } + + @Test + public void createTutorialPages_turnOnSoftwareShortcut_hasOnePage() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 1); + assertThat(alertDialog).isNotNull(); + } + + @Test + public void createTutorialPages_turnOnSoftwareAndHardwareShortcuts_hasTwoPages() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + mShortcutTypes |= UserShortcutType.HARDWARE; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 2); + assertThat(alertDialog).isNotNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java index 655e7b60b8c..b8936c4601e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java @@ -52,7 +52,7 @@ public final class AccessibilityUtilTest { private static final ComponentName DUMMY_COMPONENT_NAME2 = new ComponentName(DUMMY_PACKAGE_NAME, DUMMY_CLASS_NAME2); private static final String SOFTWARE_SHORTCUT_KEY = - Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; private static final String HARDWARE_SHORTCUT_KEY = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index e476c21f37d..3df9c048637 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -129,6 +129,11 @@ public class ToggleFeaturePreferenceFragmentTest { return 0; } + @Override + int getUserShortcutTypes() { + return 0; + } + @Override public int getPreferenceScreenResId() { return R.xml.placeholder_prefs; diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 6cf9ad6e433..10d8af78427 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -40,7 +40,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { private static final ComponentName DUMMY_COMPONENT_NAME = new ComponentName(DUMMY_PACKAGE_NAME, DUMMY_CLASS_NAME); private static final String SOFTWARE_SHORTCUT_KEY = - Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; private static final String HARDWARE_SHORTCUT_KEY = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; private static final String TRIPLETAP_SHORTCUT_KEY = diff --git a/tests/robotests/src/com/android/settings/datausage/CellDataPreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/CellDataPreferenceTest.java index 5f2a54c5bc7..c0b12dc50c1 100644 --- a/tests/robotests/src/com/android/settings/datausage/CellDataPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/datausage/CellDataPreferenceTest.java @@ -17,11 +17,14 @@ package com.android.settings.datausage; 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.doNothing; import static org.mockito.Mockito.doReturn; import android.content.Context; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; @@ -31,7 +34,6 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.network.ProxySubscriptionManager; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -40,12 +42,13 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) -@Ignore public class CellDataPreferenceTest { @Mock private ProxySubscriptionManager mProxySubscriptionMgr; @Mock + private SubscriptionManager mSubscriptionManager; + @Mock private SubscriptionInfo mSubInfo; private Context mContext; @@ -57,8 +60,20 @@ public class CellDataPreferenceTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mPreference = new CellDataPreference(mContext, null); - mPreference.mProxySubscriptionMgr = mProxySubscriptionMgr; + mPreference = new CellDataPreference(mContext, null) { + @Override + ProxySubscriptionManager getProxySubscriptionManager() { + return mProxySubscriptionMgr; + } + @Override + SubscriptionInfo getActiveSubscriptionInfo(int subId) { + return mSubInfo; + } + }; + doNothing().when(mSubscriptionManager).setDefaultDataSubId(anyInt()); + doReturn(mSubscriptionManager).when(mProxySubscriptionMgr).get(); + doNothing().when(mProxySubscriptionMgr).addActiveSubscriptionsListener(any()); + doNothing().when(mProxySubscriptionMgr).removeActiveSubscriptionsListener(any()); final LayoutInflater inflater = LayoutInflater.from(mContext); final View view = inflater.inflate(mPreference.getLayoutResource(), @@ -69,14 +84,13 @@ public class CellDataPreferenceTest { @Test public void noActiveSub_shouldDisable() { - doReturn(null).when(mProxySubscriptionMgr).getActiveSubscriptionInfo(anyInt()); + mSubInfo = null; mPreference.mOnSubscriptionsChangeListener.onChanged(); assertThat(mPreference.isEnabled()).isFalse(); } @Test public void hasActiveSub_shouldEnable() { - doReturn(mSubInfo).when(mProxySubscriptionMgr).getActiveSubscriptionInfo(anyInt()); mPreference.mOnSubscriptionsChangeListener.onChanged(); assertThat(mPreference.isEnabled()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java index d6f3e947d87..210f0fd74b5 100644 --- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java @@ -19,7 +19,6 @@ package com.android.settings.datausage; import static android.net.ConnectivityManager.TYPE_WIFI; import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; @@ -159,8 +158,6 @@ public class DataUsageSummaryPreferenceControllerTest { doReturn(CARRIER_NAME).when(mSubscriptionInfo).getCarrierName(); doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(mDefaultSubscriptionId); doReturn(mSubscriptionPlans).when(mController).getSubscriptionPlans(mDefaultSubscriptionId); - - doReturn(true).when(mController).hasSim(); } @After @@ -355,56 +352,16 @@ public class DataUsageSummaryPreferenceControllerTest { false /* isSingleWifi */); } - @Test - public void testSummaryUpdate_noSim_shouldSetWifiMode() { - mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mController.mDataUsageController = mDataUsageController; - doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephonyManager).getSimState(); - doReturn(false).when(mController).hasSim(); - - final long now = System.currentTimeMillis(); - final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now); - info.warningLevel = BillingCycleSettings.MIB_IN_BYTES; - info.limitLevel = BillingCycleSettings.MIB_IN_BYTES; - - final Intent intent = new Intent(); - - doReturn(info).when(mDataUsageController).getDataUsageInfo(any()); - setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS); - - mController.updateState(mSummaryPreference); - - verify(mSummaryPreference).setWifiMode(true /* isWifiMode */, info.period /* usagePeriod */, - false /* isSingleWifi */); - verify(mSummaryPreference).setLimitInfo(null); - verify(mSummaryPreference).setUsageNumbers(info.usageLevel, -1L, true); - verify(mSummaryPreference).setChartEnabled(false); - verify(mSummaryPreference).setUsageInfo(info.cycleEnd, -1L, null, 0, null); - } - @Test public void testMobileData_preferenceAvailable() { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } - @Test - public void testMobileData_noSimNoWifi_preferenceDisabled() { - final int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - mController.init(subscriptionId); - mController.mDataUsageController = mDataUsageController; - doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephonyManager).getSimState(); - doReturn(false).when(mController).hasSim(); - when(mConnectivityManager.isNetworkSupported(TYPE_WIFI)).thenReturn(false); - assertThat(mController.getAvailabilityStatus()) - .isEqualTo(CONDITIONALLY_UNAVAILABLE); - } - @Test public void testMobileData_noSimWifi_preferenceDisabled() { final int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; mController.init(subscriptionId); mController.mDataUsageController = mDataUsageController; - doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephonyManager).getSimState(); when(mConnectivityManager.isNetworkSupported(TYPE_WIFI)).thenReturn(true); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryTest.java index 1b21bbfc885..274c12a8f94 100644 --- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryTest.java @@ -18,6 +18,7 @@ package com.android.settings.datausage; 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.anyString; import static org.mockito.Mockito.doNothing; @@ -83,7 +84,7 @@ public class DataUsageSummaryTest { ShadowUserManager.getShadow().setIsAdminUser(true); shadowContext.setSystemService(Context.NETWORK_POLICY_SERVICE, mNetworkPolicyManager); - mContext = spy(RuntimeEnvironment.application); + mContext = RuntimeEnvironment.application; mTelephonyManager = mContext.getSystemService(TelephonyManager.class); final ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf(mTelephonyManager); shadowTelephonyManager.setTelephonyManagerForSubscriptionId( @@ -112,6 +113,7 @@ public class DataUsageSummaryTest { ShadowDataUsageUtils.HAS_SIM = true; final DataUsageSummary dataUsageSummary = spy(new DataUsageSummary()); + doNothing().when(dataUsageSummary).enableProxySubscriptionManager(any()); doReturn(mContext).when(dataUsageSummary).getContext(); doReturn(true).when(dataUsageSummary).removePreference(anyString()); @@ -125,13 +127,13 @@ public class DataUsageSummaryTest { } @Test - @Ignore public void configuration_withoutSim_shouldShowWifiSectionOnly() { ShadowDataUsageUtils.IS_MOBILE_DATA_SUPPORTED = true; ShadowDataUsageUtils.IS_WIFI_SUPPORTED = true; ShadowDataUsageUtils.HAS_SIM = false; final DataUsageSummary dataUsageSummary = spy(new DataUsageSummary()); + doNothing().when(dataUsageSummary).enableProxySubscriptionManager(any()); doReturn(mContext).when(dataUsageSummary).getContext(); doReturn(true).when(dataUsageSummary).removePreference(anyString()); @@ -145,13 +147,13 @@ public class DataUsageSummaryTest { } @Test - @Ignore public void configuration_withoutMobile_shouldShowWifiSectionOnly() { ShadowDataUsageUtils.IS_MOBILE_DATA_SUPPORTED = false; ShadowDataUsageUtils.IS_WIFI_SUPPORTED = true; ShadowDataUsageUtils.HAS_SIM = false; final DataUsageSummary dataUsageSummary = spy(new DataUsageSummary()); + doNothing().when(dataUsageSummary).enableProxySubscriptionManager(any()); doReturn(mContext).when(dataUsageSummary).getContext(); doReturn(true).when(dataUsageSummary).removePreference(anyString()); @@ -166,7 +168,6 @@ public class DataUsageSummaryTest { @Test @Config(shadows = ShadowSubscriptionManager.class) - @Ignore public void configuration_invalidDataSusbscription_shouldShowWifiSectionOnly() { ShadowDataUsageUtils.IS_MOBILE_DATA_SUPPORTED = true; ShadowDataUsageUtils.IS_WIFI_SUPPORTED = true; @@ -175,6 +176,7 @@ public class DataUsageSummaryTest { SubscriptionManager.INVALID_SUBSCRIPTION_ID); final DataUsageSummary dataUsageSummary = spy(new DataUsageSummary()); + doNothing().when(dataUsageSummary).enableProxySubscriptionManager(any()); doReturn(mContext).when(dataUsageSummary).getContext(); doReturn(true).when(dataUsageSummary).removePreference(anyString()); diff --git a/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java index d6500dbb927..7c046f84161 100644 --- a/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java @@ -16,16 +16,20 @@ package com.android.settings.display; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + import android.content.Context; import android.hardware.display.ColorDisplayManager; +import android.location.LocationManager; import com.android.settings.testutils.shadow.SettingsShadowResources; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -36,10 +40,14 @@ public class NightDisplayAutoModePreferenceControllerTest { private Context mContext; private NightDisplayAutoModePreferenceController mController; + private LocationManager mLocationManager; @Before public void setUp() { - mContext = RuntimeEnvironment.application; + mContext = Mockito.spy(RuntimeEnvironment.application); + mLocationManager = Mockito.mock(LocationManager.class); + when(mLocationManager.isLocationEnabled()).thenReturn(true); + when(mContext.getSystemService(eq(LocationManager.class))).thenReturn(mLocationManager); mController = new NightDisplayAutoModePreferenceController(mContext, "night_display_auto_mode"); } @@ -64,7 +72,6 @@ public class NightDisplayAutoModePreferenceControllerTest { } @Test - @Ignore public void onPreferenceChange_changesAutoMode() { mController.onPreferenceChange(null, String.valueOf(ColorDisplayManager.AUTO_MODE_TWILIGHT)); diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java index 92ecdc83315..1bbbe7d21ed 100644 --- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java @@ -37,7 +37,6 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -102,16 +101,6 @@ public class DarkModeScheduleSelectorControllerTest { verify(mPreference).setValueIndex(0); } - @Test - @Ignore - public void nightMode_selectNightMode_locationOff() { - when(mLocationManager.isLocationEnabled()).thenReturn(false); - mController.onPreferenceChange(mPreference, - mContext.getString(R.string.dark_ui_auto_mode_never)); - assertFalse(mController.onPreferenceChange(mPreference, - mContext.getString(R.string.dark_ui_auto_mode_auto))); - } - @Test public void nightMode_updateStateNone_dropDownValueChangedToAuto() { when(mUiService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO); diff --git a/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java new file mode 100644 index 00000000000..7891d2f9427 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 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.gestures; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class PowerMenuPrivacyPreferenceControllerTest { + + private static final String TEST_KEY = "test_key"; + private static final String SETTING_KEY = Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT; + private static final String CARDS_AVAILABLE_KEY = + Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE; + private static final String CARDS_ENABLED_KEY = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED; + private static final String CONTROLS_ENABLED_KEY = Settings.Secure.CONTROLS_ENABLED; + + private Context mContext; + private ContentResolver mContentResolver; + private PowerMenuPrivacyPreferenceController mController; + + @Mock + private Preference mPreference; + @Mock + private LockPatternUtils mLockPatternUtils; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mContentResolver = mContext.getContentResolver(); + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext)) + .thenReturn(mLockPatternUtils); + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + + mController = new PowerMenuPrivacyPreferenceController(mContext, TEST_KEY); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIs1_returnTrue() { + Settings.Secure.putInt(mContentResolver, SETTING_KEY, 1); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIs0_returnFalse() { + Settings.Secure.putInt(mContentResolver, SETTING_KEY, 0); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIsNotSet_returnFalse() { + Settings.Secure.putString(mContentResolver, SETTING_KEY, null); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void setChecked_true_POWER_MENU_LOCKED_SHOW_CONTENTIsNot0() { + mController.setChecked(true); + + assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isNotEqualTo(0); + } + + @Test + public void setChecked_false_POWER_MENU_LOCKED_SHOW_CONTENTIs0() { + mController.setChecked(false); + + assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isEqualTo(0); + } + + @Test + public void getSummary_notSecureLock_isPower_menu_privacy_not_secureString() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_not_secure)); + } + + @Test + public void getSummary_cardsAvailable_isPower_menu_privacy_showString() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_show)); + } + + @Test + public void getSummary_cardsUnavailable_isPower_menu_privacy_show_controlsString() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 0); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_show_controls)); + } + + @Test + public void updateState_onPreferenceRefreshed_preferenceEnabledAndSummaryChanged() { + mController.updateState(mPreference); + + verify(mPreference).setEnabled(anyBoolean()); + verify(mPreference, atLeastOnce()).setSummary(mController.getSummary()); + } + + @Test + public void getAvailabilityStatus_allOff_returnsDisabled() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } + + @Test + public void getAvailabilityStatus_cardsUnavailableControlsOff_returnsDisabled() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } + + @Test + public void testAvailabilityStatus_cardsOnControlsOff_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_cardsOffControlsOn_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_allOn_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_allOnNotSecure_returnsDisabled() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } +} diff --git a/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragmentTest.java b/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragmentTest.java index 6aef69955c6..f2db71fe406 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragmentTest.java @@ -48,6 +48,7 @@ import org.robolectric.shadows.ShadowAlertDialog; @Config(shadows = ShadowAlertDialog.class) public class ContactDiscoveryDialogFragmentTest { private static final int TEST_SUB_ID = 2; + private static final String TEST_CARRIER = "TestMobile"; @Mock private ImsManager mImsManager; @Mock private ImsRcsManager mImsRcsManager; @@ -60,7 +61,8 @@ public class ContactDiscoveryDialogFragmentTest { public void setUp() { MockitoAnnotations.initMocks(this); mActivity = Robolectric.buildActivity(FragmentActivity.class).setup().get(); - mDialogFragmentUT = spy(ContactDiscoveryDialogFragment.newInstance(TEST_SUB_ID)); + mDialogFragmentUT = spy(ContactDiscoveryDialogFragment.newInstance(TEST_SUB_ID, + TEST_CARRIER)); doReturn(mImsManager).when(mDialogFragmentUT).getImsManager(any()); doReturn(mImsRcsManager).when(mImsManager).getImsRcsManager(TEST_SUB_ID); doReturn(mRcsUceAdapter).when(mImsRcsManager).getUceAdapter(); diff --git a/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceControllerTest.java index 0370bfa00c6..3d54cfbae03 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceControllerTest.java @@ -48,6 +48,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.LifecycleOwner; import androidx.preference.SwitchPreference; +import com.android.settings.network.SubscriptionUtil; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -59,6 +60,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Collections; + @RunWith(RobolectricTestRunner.class) public class ContactDiscoveryPreferenceControllerTest { @@ -99,6 +102,8 @@ public class ContactDiscoveryPreferenceControllerTest { mPreferenceControllerUT = new ContactDiscoveryPreferenceController(mContext, "ContactDiscovery"); + // Ensure subscriptionInfo check doesn't fail. + SubscriptionUtil.setAvailableSubscriptionsForTesting(Collections.emptyList()); mPreferenceControllerUT.init(mFragmentManager, TEST_SUB_ID, mLifecycle); mSwitchPreferenceUT = spy(new SwitchPreference(mContext)); mSwitchPreferenceUT.setKey(mPreferenceControllerUT.getPreferenceKey()); diff --git a/tests/robotests/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.java index 7681f447314..43bda4e3e27 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.java @@ -17,12 +17,12 @@ package com.android.settings.network.telephony; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import android.app.Activity; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.Intent; @@ -30,21 +30,22 @@ import android.net.TrafficStats; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; + import androidx.preference.SwitchPreference; + import com.android.settings.core.BasePreferenceController; import com.android.settingslib.net.DataUsageController; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.shadows.ShadowTelephonyManager; -import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class DataUsagePreferenceControllerTest { @@ -71,7 +72,7 @@ public class DataUsagePreferenceControllerTest { doReturn(mNetworkStatsManager).when(mContext).getSystemService(NetworkStatsManager.class); mPreference = new SwitchPreference(mContext); - mController = new DataUsagePreferenceController(mContext, "data_usage"); + mController = spy(new DataUsagePreferenceController(mContext, "data_usage")); mController.init(SUB_ID); mPreference.setKey(mController.getPreferenceKey()); } @@ -114,8 +115,9 @@ public class DataUsagePreferenceControllerTest { @Test public void updateState_noUsageData_shouldDisablePreference() { - ReflectionHelpers.setField( - mController, "mDataUsageInfo", new DataUsageController.DataUsageInfo()); + final DataUsageController.DataUsageInfo usageInfo = + new DataUsageController.DataUsageInfo(); + doReturn(usageInfo).when(mController).getDataUsageInfo(any()); mController.updateState(mPreference); @@ -124,9 +126,10 @@ public class DataUsagePreferenceControllerTest { @Test public void updateState_shouldUseIECUnit() { - final DataUsageController.DataUsageInfo usageInfo = new DataUsageController.DataUsageInfo(); + final DataUsageController.DataUsageInfo usageInfo = + new DataUsageController.DataUsageInfo(); usageInfo.usageLevel = TrafficStats.MB_IN_BYTES; - ReflectionHelpers.setField(mController, "mDataUsageInfo", usageInfo); + doReturn(usageInfo).when(mController).getDataUsageInfo(any()); mController.updateState(mPreference); diff --git a/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 9a3b526dd7a..e0013e08f8c 100644 --- a/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -86,5 +86,10 @@ public class ToggleFeaturePreferenceFragmentTest { public int getMetricsCategory() { return 0; } + + @Override + int getUserShortcutTypes() { + return 0; + } } } \ No newline at end of file