diff --git a/res/drawable/button_border_selected.xml b/res/drawable/button_border_selected.xml index 65dfe1b67a3..29acbcea8e1 100644 --- a/res/drawable/button_border_selected.xml +++ b/res/drawable/button_border_selected.xml @@ -20,6 +20,6 @@ android:color="@color/notification_importance_selection_bg" /> + android:color="?android:attr/colorAccent"/> diff --git a/res/drawable/ic_bubble_all.xml b/res/drawable/ic_bubble_all.xml new file mode 100644 index 00000000000..fdcf6dc803c --- /dev/null +++ b/res/drawable/ic_bubble_all.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/res/drawable/ic_bubble_none.xml b/res/drawable/ic_bubble_none.xml new file mode 100644 index 00000000000..e8fd7df3def --- /dev/null +++ b/res/drawable/ic_bubble_none.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/res/drawable/ic_bubble_selected.xml b/res/drawable/ic_bubble_selected.xml new file mode 100644 index 00000000000..f9533283b5e --- /dev/null +++ b/res/drawable/ic_bubble_selected.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/res/drawable/ic_create_bubble.xml b/res/drawable/ic_create_bubble.xml index e9433553163..82d5db81210 100644 --- a/res/drawable/ic_create_bubble.xml +++ b/res/drawable/ic_create_bubble.xml @@ -14,17 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + + \ No newline at end of file diff --git a/res/layout/accessibility_edit_magnification_mode.xml b/res/layout/accessibility_edit_magnification_mode.xml index bdc355d2551..e4f31328fae 100644 --- a/res/layout/accessibility_edit_magnification_mode.xml +++ b/res/layout/accessibility_edit_magnification_mode.xml @@ -28,12 +28,19 @@ android:padding="24dp"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/accessibility_magnification_area_settings_message" + android:textAppearance="?android:attr/textAppearanceListItemSecondary" + android:textColor="?android:attr/textColorSecondary" + android:layout_marginBottom="24dp"/> + layout="@layout/accessibility_edit_shortcut_component" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="32dp" /> + android:scaleType="fitCenter" /> diff --git a/res/layout/bubble_preference.xml b/res/layout/bubble_preference.xml new file mode 100644 index 00000000000..8a64716b757 --- /dev/null +++ b/res/layout/bubble_preference.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/contextual_slice_full_tile.xml b/res/layout/contextual_slice_full_tile.xml index 4b1155e3e97..b6ab410f341 100644 --- a/res/layout/contextual_slice_full_tile.xml +++ b/res/layout/contextual_slice_full_tile.xml @@ -19,7 +19,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/ContextualCardStyle"> + style="@style/ContextualCardStyle" + android:importantForAccessibility="no"> @@ -28,10 +29,9 @@ android:theme="@style/Theme.Settings.ContextualCard" style="@style/ContextualCardSliceViewStyle" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:importantForAccessibility="no"/> + android:layout_height="wrap_content"/> - + \ No newline at end of file diff --git a/res/layout/contextual_slice_sticky_tile.xml b/res/layout/contextual_slice_sticky_tile.xml index 5991068dfc6..2e7a2befa63 100644 --- a/res/layout/contextual_slice_sticky_tile.xml +++ b/res/layout/contextual_slice_sticky_tile.xml @@ -19,14 +19,14 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/ContextualCardStyle"> + style="@style/ContextualCardStyle" + android:importantForAccessibility="no"> + android:layout_height="wrap_content"/> diff --git a/res/raw-night/bubble_notification_animation.mp4 b/res/raw-night/bubble_notification_animation.mp4 new file mode 100644 index 00000000000..1a25e9378ad Binary files /dev/null and b/res/raw-night/bubble_notification_animation.mp4 differ diff --git a/res/raw/bubble_notification_animation.mp4 b/res/raw/bubble_notification_animation.mp4 new file mode 100644 index 00000000000..299454850e4 Binary files /dev/null and b/res/raw/bubble_notification_animation.mp4 differ diff --git a/res/xml/app_bubble_notification_settings.xml b/res/xml/app_bubble_notification_settings.xml index 8d97f8fda7a..3f52ad38153 100644 --- a/res/xml/app_bubble_notification_settings.xml +++ b/res/xml/app_bubble_notification_settings.xml @@ -13,22 +13,18 @@ See the License for the specific language governing permissions and limitations under the License. --> - + - - - + android:title="@string/notification_bubbles_title" + settings:allowDividerBelow="false"/> diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml index ceb08a26e32..f0200ce46b5 100644 --- a/res/xml/app_notification_settings.xml +++ b/res/xml/app_notification_settings.xml @@ -29,6 +29,14 @@ + + + + - + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml index 3dcddc8eb19..cb8357bdb0e 100644 --- a/res/xml/configure_notification_settings.xml +++ b/res/xml/configure_notification_settings.xml @@ -50,15 +50,23 @@ - - - + settings:allowDividerAbove="true" + android:summary="@string/manage_conversations" + android:order="6" + android:fragment="com.android.settings.notification.app.ConversationListSettings" + /> + + + settings:controller="com.android.settings.network.telephony.NetworkPreferenceCategoryController"> updatePreference()); + } + + private void updatePreference() { + if (mPreferenceScreen != null) { + displayPreference(mPreferenceScreen); + } + if (mPreference != null) { + updateState(mPreference); + } } @Override @@ -94,20 +100,18 @@ public class EnabledNetworkModePreferenceController extends @OnLifecycleEvent(ON_START) public void onStart() { - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + mSubId), - true, - mPreferredNetworkModeObserver); + mPreferredNetworkModeObserver.register(mContext, mSubId); } @OnLifecycleEvent(ON_STOP) public void onStop() { - mContext.getContentResolver().unregisterContentObserver(mPreferredNetworkModeObserver); + mPreferredNetworkModeObserver.unregister(mContext); } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mPreferenceScreen = screen; mPreference = screen.findPreference(getPreferenceKey()); } diff --git a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java index d3fb437a36c..cbe0912f0e9 100644 --- a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java +++ b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.network.telephony; import android.content.Context; +import android.os.Looper; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; @@ -187,7 +188,7 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc private class PhoneCallStateListener extends PhoneStateListener { PhoneCallStateListener() { - super(); + super(Looper.getMainLooper()); } private TelephonyManager mTelephonyManager; diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 6ca556ade6a..4103447f642 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -44,7 +44,6 @@ import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceCon import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController; import com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController; import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.widget.PreferenceCategoryController; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.SearchIndexable; @@ -155,14 +154,13 @@ public class MobileNetworkSettings extends RestrictedDashboardFragment { use(WifiCallingPreferenceController.class).init(mSubId); final OpenNetworkSelectPagePreferenceController openNetworkSelectPagePreferenceController = - use(OpenNetworkSelectPagePreferenceController.class).init(mSubId); + use(OpenNetworkSelectPagePreferenceController.class).init(getLifecycle(), mSubId); final AutoSelectPreferenceController autoSelectPreferenceController = use(AutoSelectPreferenceController.class) - .init(mSubId) + .init(getLifecycle(), mSubId) .addListener(openNetworkSelectPagePreferenceController); - use(PreferenceCategoryController.class).setChildren( - Arrays.asList(autoSelectPreferenceController)); - + use(NetworkPreferenceCategoryController.class).init(getLifecycle(), mSubId) + .setChildren(Arrays.asList(autoSelectPreferenceController)); mCdmaSystemSelectPreferenceController = use(CdmaSystemSelectPreferenceController.class); mCdmaSystemSelectPreferenceController.init(getPreferenceManager(), mSubId); mCdmaSubscriptionPreferenceController = use(CdmaSubscriptionPreferenceController.class); diff --git a/src/com/android/settings/network/telephony/NetworkPreferenceCategoryController.java b/src/com/android/settings/network/telephony/NetworkPreferenceCategoryController.java new file mode 100644 index 00000000000..042eb5fc088 --- /dev/null +++ b/src/com/android/settings/network/telephony/NetworkPreferenceCategoryController.java @@ -0,0 +1,80 @@ +/* + * 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.network.telephony; + +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.telephony.SubscriptionManager; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.PreferenceScreen; + +import com.android.settings.network.PreferredNetworkModeContentObserver; +import com.android.settings.widget.PreferenceCategoryController; + +/** + * Preference controller for "Network" category + */ +public class NetworkPreferenceCategoryController extends PreferenceCategoryController + implements LifecycleObserver { + + private PreferenceScreen mPreferenceScreen; + private PreferredNetworkModeContentObserver mPreferredNetworkModeObserver; + protected int mSubId; + + public NetworkPreferenceCategoryController(Context context, String key) { + super(context, key); + mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + mPreferredNetworkModeObserver = new PreferredNetworkModeContentObserver( + new Handler(Looper.getMainLooper())); + mPreferredNetworkModeObserver.setPreferredNetworkModeChangedListener( + () -> updatePreference()); + } + + private void updatePreference() { + displayPreference(mPreferenceScreen); + } + + @OnLifecycleEvent(ON_START) + public void onStart() { + mPreferredNetworkModeObserver.register(mContext, mSubId); + } + + @OnLifecycleEvent(ON_STOP) + public void onStop() { + mPreferredNetworkModeObserver.unregister(mContext); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceScreen = screen; + } + + public NetworkPreferenceCategoryController init(Lifecycle lifecycle, int subId) { + mSubId = subId; + + lifecycle.addObserver(this); + return this; + } +} diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java index eed40467c84..d235a9ac36a 100644 --- a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.network.telephony; import android.content.Context; +import android.os.Looper; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; @@ -164,7 +165,7 @@ public class VideoCallingPreferenceController extends TelephonyTogglePreferenceC private class PhoneCallStateListener extends PhoneStateListener { PhoneCallStateListener() { - super(); + super(Looper.getMainLooper()); } private TelephonyManager mTelephonyManager; diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java index e928dba734c..bec8a51adda 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Looper; import android.os.PersistableBundle; import android.provider.Settings; import android.telecom.PhoneAccountHandle; @@ -194,7 +195,7 @@ public class WifiCallingPreferenceController extends TelephonyBasePreferenceCont private class PhoneCallStateListener extends PhoneStateListener { PhoneCallStateListener() { - super(); + super(Looper.getMainLooper()); } private TelephonyManager mTelephonyManager; diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java index 2eda9d94598..361f58e4172 100644 --- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java @@ -16,6 +16,9 @@ package com.android.settings.network.telephony.gsm; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + import android.app.ProgressDialog; import android.app.settings.SettingsEnums; import android.content.Context; @@ -30,12 +33,16 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.network.PreferredNetworkModeContentObserver; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.NetworkSelectSettings; import com.android.settings.network.telephony.TelephonyTogglePreferenceController; @@ -48,10 +55,13 @@ import java.util.concurrent.TimeUnit; /** * Preference controller for "Auto Select Network" */ -public class AutoSelectPreferenceController extends TelephonyTogglePreferenceController { +public class AutoSelectPreferenceController extends TelephonyTogglePreferenceController + implements LifecycleObserver{ private static final long MINIMUM_DIALOG_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1); private final Handler mUiHandler; + private PreferenceScreen mPreferenceScreen; + private PreferredNetworkModeContentObserver mPreferredNetworkModeObserver; private TelephonyManager mTelephonyManager; private boolean mOnlyAutoSelectInHome; private List mListeners; @@ -66,6 +76,28 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; mListeners = new ArrayList<>(); mUiHandler = new Handler(Looper.getMainLooper()); + mPreferredNetworkModeObserver = new PreferredNetworkModeContentObserver(mUiHandler); + mPreferredNetworkModeObserver.setPreferredNetworkModeChangedListener( + () -> updatePreference()); + } + + private void updatePreference() { + if (mPreferenceScreen != null) { + displayPreference(mPreferenceScreen); + } + if (mSwitchPreference != null) { + updateState(mSwitchPreference); + } + } + + @OnLifecycleEvent(ON_START) + public void onStart() { + mPreferredNetworkModeObserver.register(mContext, mSubId); + } + + @OnLifecycleEvent(ON_STOP) + public void onStop() { + mPreferredNetworkModeObserver.unregister(mContext); } @Override @@ -78,6 +110,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mPreferenceScreen = screen; mSwitchPreference = screen.findPreference(getPreferenceKey()); } @@ -142,7 +175,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon } } - public AutoSelectPreferenceController init(int subId) { + public AutoSelectPreferenceController init(Lifecycle lifecycle, int subId) { mSubId = subId; mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); @@ -153,6 +186,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL) : false; + lifecycle.addObserver(this); return this; } diff --git a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java index 8c3928d7d50..2cc5bf1bbef 100644 --- a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java @@ -16,38 +16,64 @@ package com.android.settings.network.telephony.gsm; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.provider.Settings; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.network.PreferredNetworkModeContentObserver; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.NetworkSelectSettings; import com.android.settings.network.telephony.TelephonyBasePreferenceController; + /** * Preference controller for "Open network select" */ public class OpenNetworkSelectPagePreferenceController extends TelephonyBasePreferenceController implements - AutoSelectPreferenceController.OnNetworkSelectModeListener { + AutoSelectPreferenceController.OnNetworkSelectModeListener, LifecycleObserver { private TelephonyManager mTelephonyManager; private Preference mPreference; + private PreferenceScreen mPreferenceScreen; + private PreferredNetworkModeContentObserver mPreferredNetworkModeObserver; public OpenNetworkSelectPagePreferenceController(Context context, String key) { super(context, key); mTelephonyManager = context.getSystemService(TelephonyManager.class); mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + mPreferredNetworkModeObserver = new PreferredNetworkModeContentObserver( + new Handler(Looper.getMainLooper())); + mPreferredNetworkModeObserver.setPreferredNetworkModeChangedListener( + () -> updatePreference()); + + } + + private void updatePreference() { + if (mPreferenceScreen != null) { + displayPreference(mPreferenceScreen); + } + if (mPreference != null) { + updateState(mPreference); + } } @Override @@ -57,9 +83,20 @@ public class OpenNetworkSelectPagePreferenceController extends : CONDITIONALLY_UNAVAILABLE; } + @OnLifecycleEvent(ON_START) + public void onStart() { + mPreferredNetworkModeObserver.register(mContext, mSubId); + } + + @OnLifecycleEvent(ON_STOP) + public void onStop() { + mPreferredNetworkModeObserver.unregister(mContext); + } + @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mPreferenceScreen = screen; mPreference = screen.findPreference(getPreferenceKey()); } @@ -97,10 +134,11 @@ public class OpenNetworkSelectPagePreferenceController extends return false; } - public OpenNetworkSelectPagePreferenceController init(int subId) { + public OpenNetworkSelectPagePreferenceController init(Lifecycle lifecycle, int subId) { mSubId = subId; mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); + lifecycle.addObserver(this); return this; } diff --git a/src/com/android/settings/notification/BubbleNotificationPreferenceController.java b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java new file mode 100644 index 00000000000..0fa480cb64a --- /dev/null +++ b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java @@ -0,0 +1,132 @@ +/* + * 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; + +import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.TogglePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +/** + * Feature level screen for bubbles, available through notification menu. + * Allows user to turn bubbles on or off for the device. + */ +public class BubbleNotificationPreferenceController extends TogglePreferenceController + implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, + LifecycleObserver, OnResume, OnPause { + + private static final String TAG = "BubbleNotifPrefContr"; + + @VisibleForTesting + static final int ON = 1; + @VisibleForTesting + static final int OFF = 0; + + private SettingObserver mSettingObserver; + + public BubbleNotificationPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Preference preference = screen.findPreference(getPreferenceKey()); + if (preference != null) { + mSettingObserver = new SettingObserver(preference); + } + } + + @Override + public void onResume() { + if (mSettingObserver != null) { + mSettingObserver.register(mContext.getContentResolver(), true /* register */); + } + } + + @Override + public void onPause() { + if (mSettingObserver != null) { + mSettingObserver.register(mContext.getContentResolver(), false /* register */); + } + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public boolean isChecked() { + return Settings.Global.getInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, ON) == ON; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Global.putInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, isChecked ? ON : OFF); + } + + @Override + public boolean isSliceable() { + return false; + } + + class SettingObserver extends ContentObserver { + + private final Uri NOTIFICATION_BUBBLES_URI = + Settings.Global.getUriFor(NOTIFICATION_BUBBLES); + + private final Preference mPreference; + + SettingObserver(Preference preference) { + super(new Handler()); + mPreference = preference; + } + + public void register(ContentResolver cr, boolean register) { + if (register) { + cr.registerContentObserver(NOTIFICATION_BUBBLES_URI, false, this); + } else { + cr.unregisterContentObserver(this); + } + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (NOTIFICATION_BUBBLES_URI.equals(uri)) { + updateState(mPreference); + } + } + } +} diff --git a/src/com/android/settings/notification/BubbleNotificationSettings.java b/src/com/android/settings/notification/BubbleNotificationSettings.java new file mode 100644 index 00000000000..6e04683c4cd --- /dev/null +++ b/src/com/android/settings/notification/BubbleNotificationSettings.java @@ -0,0 +1,52 @@ +/* + * 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; + +import android.app.settings.SettingsEnums; + +import com.android.settings.R; +import com.android.settings.core.OnActivityResultListener; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +@SearchIndexable +public class BubbleNotificationSettings extends DashboardFragment implements + OnActivityResultListener { + private static final String TAG = "BubbleNotiSettings"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.BUBBLE_SETTINGS; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.bubble_notification_settings; + } + + /** + * For Search. + */ + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.bubble_notification_settings); +} diff --git a/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java b/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java new file mode 100644 index 00000000000..f123c51fbdc --- /dev/null +++ b/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java @@ -0,0 +1,58 @@ +/* + * 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; + +import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; + +import android.content.Context; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; + +/** + * Summary of the feature setting for bubbles, available through notification menu. + */ +public class BubbleSummaryNotificationPreferenceController extends BasePreferenceController { + + @VisibleForTesting + static final int ON = 1; + + public BubbleSummaryNotificationPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public CharSequence getSummary() { + return mContext.getString( + areBubblesEnabled() + ? R.string.notifications_bubble_setting_on_summary + : R.string.switch_off_text); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + private boolean areBubblesEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, ON) == ON; + } +} diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index fb0a438843f..b172879b64e 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -24,6 +24,7 @@ import android.app.INotificationManager; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationHistory; +import android.app.NotificationManager; import android.app.role.RoleManager; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; @@ -81,7 +82,7 @@ public class NotificationBackend { row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app); row.banned = getNotificationsBanned(row.pkg, row.uid); row.showBadge = canShowBadge(row.pkg, row.uid); - row.allowBubbles = canBubble(row.pkg, row.uid); + row.bubblePreference = getBubblePreference(row.pkg, row.uid); row.userId = UserHandle.getUserId(row.uid); row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid); row.channelCount = getChannelCount(row.pkg, row.uid); @@ -192,18 +193,18 @@ public class NotificationBackend { } } - public boolean canBubble(String pkg, int uid) { + public int getBubblePreference(String pkg, int uid) { try { - return sINM.areBubblesAllowedForPackage(pkg, uid); + return sINM.getBubblePreferenceForPackage(pkg, uid); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); - return false; + return -1; } } - public boolean setAllowBubbles(String pkg, int uid, boolean allow) { + public boolean setAllowBubbles(String pkg, int uid, int preference) { try { - sINM.setBubblesAllowed(pkg, uid, allow); + sINM.setBubblesAllowed(pkg, uid, preference); return true; } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); @@ -563,7 +564,7 @@ public class NotificationBackend { public boolean systemApp; public boolean lockedImportance; public boolean showBadge; - public boolean allowBubbles; + public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE; public int userId; public int blockedChannelCount; public int channelCount; diff --git a/src/com/android/settings/notification/app/AppBubbleNotificationSettings.java b/src/com/android/settings/notification/app/AppBubbleNotificationSettings.java index 0ed1b84bdc5..5026a26a52a 100644 --- a/src/com/android/settings/notification/app/AppBubbleNotificationSettings.java +++ b/src/com/android/settings/notification/app/AppBubbleNotificationSettings.java @@ -30,6 +30,9 @@ import com.android.settingslib.search.SearchIndexable; import java.util.ArrayList; import java.util.List; +/** + * App level settings for bubbles. + */ @SearchIndexable public class AppBubbleNotificationSettings extends NotificationSettings implements GlobalBubblePermissionObserverMixin.Listener { diff --git a/src/com/android/settings/notification/app/AppNotificationSettings.java b/src/com/android/settings/notification/app/AppNotificationSettings.java index d9f42393597..a4228411a72 100644 --- a/src/com/android/settings/notification/app/AppNotificationSettings.java +++ b/src/com/android/settings/notification/app/AppNotificationSettings.java @@ -22,6 +22,10 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settingslib.core.AbstractPreferenceController; @@ -29,10 +33,6 @@ import com.android.settingslib.core.AbstractPreferenceController; import java.util.ArrayList; import java.util.List; -import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceScreen; - /** These settings are per app, so should not be returned in global search results. */ public class AppNotificationSettings extends NotificationSettings { private static final String TAG = "AppNotificationSettings"; @@ -41,8 +41,7 @@ public class AppNotificationSettings extends NotificationSettings { private static String KEY_ADVANCED_CATEGORY = "app_advanced"; private static String KEY_BADGE = "badge"; private static String KEY_APP_LINK = "app_link"; - private static String KEY_BUBBLE = "bubble_link_pref"; - private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK, KEY_BUBBLE}; + private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK}; @Override public int getMetricsCategory() { @@ -121,9 +120,9 @@ public class AppNotificationSettings extends NotificationSettings { mControllers.add(new DescriptionPreferenceController(context)); mControllers.add(new NotificationsOffPreferenceController(context)); mControllers.add(new DeletedChannelsPreferenceController(context, mBackend)); - mControllers.add(new BubbleSummaryPreferenceController(context, mBackend)); mControllers.add(new ChannelListPreferenceController(context, mBackend)); mControllers.add(new AppConversationListPreferenceController(context, mBackend)); + mControllers.add(new BubbleSummaryPreferenceController(context, mBackend)); return new ArrayList<>(mControllers); } } diff --git a/src/com/android/settings/notification/app/BubblePreference.java b/src/com/android/settings/notification/app/BubblePreference.java new file mode 100644 index 00000000000..679b663c8dd --- /dev/null +++ b/src/com/android/settings/notification/app/BubblePreference.java @@ -0,0 +1,172 @@ +/* + * 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 static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.Utils; +import com.android.settingslib.R; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedPreferenceHelper; + +/** + * A tri-state preference allowing a user to specify what gets to bubble. + */ +public class BubblePreference extends Preference implements View.OnClickListener { + RestrictedPreferenceHelper mHelper; + + private int mSelectedPreference; + + private Context mContext; + private Drawable mSelectedBackground; + private Drawable mUnselectedBackground; + + private ButtonViewHolder mBubbleAllButton; + private ButtonViewHolder mBubbleSelectedButton; + private ButtonViewHolder mBubbleNoneButton; + + public BubblePreference(Context context) { + this(context, null); + } + + public BubblePreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BubblePreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public BubblePreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + mHelper.useAdminDisabledSummary(true); + mContext = context; + mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected); + mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected); + setLayoutResource(R.layout.bubble_preference); + } + + public void setSelectedPreference(int preference) { + mSelectedPreference = preference; + } + + public int getSelectedPreference() { + return mSelectedPreference; + } + + public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + @Override + public void onBindViewHolder(final PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final boolean disabledByAdmin = mHelper.isDisabledByAdmin(); + View summary = holder.findViewById(android.R.id.summary); + if (disabledByAdmin) { + mHelper.onBindViewHolder(holder); + summary.setVisibility(View.VISIBLE); + } else { + summary.setVisibility(View.GONE); + } + holder.itemView.setClickable(false); + + View bubbleAll = holder.findViewById(R.id.bubble_all); + ImageView bubbleAllImage = (ImageView) holder.findViewById(R.id.bubble_all_icon); + TextView bubbleAllText = (TextView) holder.findViewById(R.id.bubble_all_label); + mBubbleAllButton = new ButtonViewHolder(bubbleAll, bubbleAllImage, bubbleAllText, + BUBBLE_PREFERENCE_ALL); + mBubbleAllButton.setSelected(mContext, mSelectedPreference == BUBBLE_PREFERENCE_ALL); + bubbleAll.setTag(BUBBLE_PREFERENCE_ALL); + bubbleAll.setOnClickListener(this); + bubbleAll.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE); + + View bubbleSelected = holder.findViewById(R.id.bubble_selected); + ImageView bubbleSelectedImage = (ImageView) holder.findViewById(R.id.bubble_selected_icon); + TextView bubbleSelectedText = (TextView) holder.findViewById(R.id.bubble_selected_label); + mBubbleSelectedButton = new ButtonViewHolder(bubbleSelected, bubbleSelectedImage, + bubbleSelectedText, BUBBLE_PREFERENCE_SELECTED); + mBubbleSelectedButton.setSelected(mContext, + mSelectedPreference == BUBBLE_PREFERENCE_SELECTED); + bubbleSelected.setTag(BUBBLE_PREFERENCE_SELECTED); + bubbleSelected.setOnClickListener(this); + bubbleSelected.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE); + + View bubbleNone = holder.findViewById(R.id.bubble_none); + ImageView bubbleNoneImage = (ImageView) holder.findViewById(R.id.bubble_none_icon); + TextView bubbleNoneText = (TextView) holder.findViewById(R.id.bubble_none_label); + mBubbleNoneButton = new ButtonViewHolder(bubbleNone, bubbleNoneImage, bubbleNoneText, + BUBBLE_PREFERENCE_NONE); + mBubbleNoneButton.setSelected(mContext, mSelectedPreference == BUBBLE_PREFERENCE_NONE); + bubbleNone.setTag(BUBBLE_PREFERENCE_NONE); + bubbleNone.setOnClickListener(this); + bubbleNone.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE); + } + + @Override + public void onClick(View v) { + final int selected = (int) v.getTag(); + callChangeListener(selected); + + mBubbleAllButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_ALL); + mBubbleSelectedButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_SELECTED); + mBubbleNoneButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_NONE); + } + + private class ButtonViewHolder { + private View mView; + private ImageView mImageView; + private TextView mTextView; + private int mId; + + ButtonViewHolder(View v, ImageView iv, TextView tv, int identifier) { + mView = v; + mImageView = iv; + mTextView = tv; + mId = identifier; + } + + void setSelected(Context context, boolean selected) { + mView.setBackground(selected ? mSelectedBackground : mUnselectedBackground); + mView.setSelected(selected); + + ColorStateList stateList = selected + ? Utils.getColorAccent(context) + : Utils.getColorAttr(context, android.R.attr.textColorPrimary); + mImageView.setImageTintList(stateList); + mTextView.setTextColor(stateList); + } + } +} diff --git a/src/com/android/settings/notification/app/BubblePreferenceController.java b/src/com/android/settings/notification/app/BubblePreferenceController.java index d33ba7e1aa3..3255192a912 100644 --- a/src/com/android/settings/notification/app/BubblePreferenceController.java +++ b/src/com/android/settings/notification/app/BubblePreferenceController.java @@ -16,6 +16,7 @@ package com.android.settings.notification.app; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; import android.annotation.Nullable; @@ -26,11 +27,14 @@ import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; -import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.notification.NotificationBackend; import com.android.settingslib.RestrictedSwitchPreference; +/** + * Preference controller for Bubbles. This is used as the app-specific page and conversation + * settings. + */ public class BubblePreferenceController extends NotificationPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { @@ -74,40 +78,49 @@ public class BubblePreferenceController extends NotificationPreferenceController return true; } + @Override public void updateState(Preference preference) { - if (mAppRow != null) { - RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; + if (mIsAppPage && mAppRow != null) { + // We're on the app specific bubble page which displays a tri-state + int backEndPref = mAppRow.bubblePreference; + BubblePreference pref = (BubblePreference) preference; pref.setDisabledByAdmin(mAdmin); - if (mChannel != null) { - pref.setChecked(mChannel.canBubble() && isGloballyEnabled()); - pref.setEnabled(!pref.isDisabledByAdmin()); + if (!isGloballyEnabled()) { + pref.setSelectedPreference(BUBBLE_PREFERENCE_NONE); } else { - pref.setChecked(mAppRow.allowBubbles && isGloballyEnabled()); - pref.setSummary(mContext.getString( - R.string.bubbles_app_toggle_summary, mAppRow.label)); + pref.setSelectedPreference(backEndPref); } + } else if (mChannel != null) { + // We're on the channel specific notification page which displays a toggle. + RestrictedSwitchPreference switchpref = (RestrictedSwitchPreference) preference; + switchpref.setDisabledByAdmin(mAdmin); + switchpref.setChecked(mChannel.canBubble() && isGloballyEnabled()); } } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean value = (Boolean) newValue && isGloballyEnabled(); if (mChannel != null) { - mChannel.setAllowBubbles(value); + // Channel page is toggle + mChannel.setAllowBubbles((boolean) newValue); saveChannel(); - return true; - } else if (mAppRow != null && mFragmentManager != null) { - RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; - // if the global setting is off, toggling app level permission requires extra - // confirmation - if (!isGloballyEnabled() && !pref.isChecked()) { - new BubbleWarningDialogFragment() - .setPkgInfo(mAppRow.pkg, mAppRow.uid) - .show(mFragmentManager, "dialog"); - return false; - } else { - mAppRow.allowBubbles = value; - mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value); + } else if (mIsAppPage) { + // App page is bubble preference + BubblePreference pref = (BubblePreference) preference; + if (mAppRow != null && mFragmentManager != null) { + final int value = (int) newValue; + if (!isGloballyEnabled() + && pref.getSelectedPreference() == BUBBLE_PREFERENCE_NONE) { + // if the global setting is off, toggling app level permission requires extra + // confirmation + new BubbleWarningDialogFragment() + .setPkgPrefInfo(mAppRow.pkg, mAppRow.uid, value) + .show(mFragmentManager, "dialog"); + return false; + } else { + mAppRow.bubblePreference = value; + mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value); + } } } return true; @@ -118,21 +131,26 @@ public class BubblePreferenceController extends NotificationPreferenceController NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON; } - // Used in app level prompt that confirms the user is ok with turning on bubbles - // globally. If they aren't, undo what + /** + * Used in app level prompt that confirms the user is ok with turning on bubbles + * globally. If they aren't, undo that. + */ public static void revertBubblesApproval(Context mContext, String pkg, int uid) { NotificationBackend backend = new NotificationBackend(); - backend.setAllowBubbles(pkg, uid, false); + backend.setAllowBubbles(pkg, uid, BUBBLE_PREFERENCE_NONE); + // changing the global settings will cause the observer on the host page to reload // correct preference state Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); } - // Apply global bubbles approval - public static void applyBubblesApproval(Context mContext, String pkg, int uid) { + /** + * Apply global bubbles approval + */ + public static void applyBubblesApproval(Context mContext, String pkg, int uid, int pref) { NotificationBackend backend = new NotificationBackend(); - backend.setAllowBubbles(pkg, uid, true); + backend.setAllowBubbles(pkg, uid, pref); // changing the global settings will cause the observer on the host page to reload // correct preference state Settings.Global.putInt(mContext.getContentResolver(), diff --git a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java index f13613c4daf..06c6f3a1a84 100644 --- a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java +++ b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -16,38 +16,34 @@ package com.android.settings.notification.app; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; -import android.app.settings.SettingsEnums; import android.content.Context; -import android.os.Bundle; +import android.content.Intent; +import android.content.res.Resources; import android.provider.Settings; -import com.android.settings.R; -import com.android.settings.applications.AppInfoBase; -import com.android.settings.core.SubSettingLauncher; -import com.android.settings.notification.NotificationBackend; - import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; -public class BubbleSummaryPreferenceController extends NotificationPreferenceController { +import com.android.settings.R; +import com.android.settings.notification.NotificationBackend; + +/** + * Summary of the app setting for bubbles, available through app notification settings. + */ +public class BubbleSummaryPreferenceController extends NotificationPreferenceController { + private static final String KEY = "bubble_pref_link"; - private static final String KEY = "bubble_link_pref"; @VisibleForTesting - static final int SYSTEM_WIDE_ON = 1; - @VisibleForTesting - static final int SYSTEM_WIDE_OFF = 0; + static final int ON = 1; public BubbleSummaryPreferenceController(Context context, NotificationBackend backend) { super(context, backend); } - @Override - public String getPreferenceKey() { - return KEY; - } - @Override public boolean isAvailable() { if (!super.isAvailable()) { @@ -63,45 +59,47 @@ public class BubbleSummaryPreferenceController extends NotificationPreferenceCon if (isDefaultChannel()) { return true; } else { - return mAppRow != null && mAppRow.allowBubbles; + return mAppRow != null; } } return isGloballyEnabled(); } + @Override + public String getPreferenceKey() { + return KEY; + } + @Override public void updateState(Preference preference) { super.updateState(preference); if (mAppRow != null) { - Bundle args = new Bundle(); - args.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg); - args.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid); - - preference.setIntent(new SubSettingLauncher(mContext) - .setDestination(AppBubbleNotificationSettings.class.getName()) - .setArguments(args) - .setSourceMetricsCategory( - SettingsEnums.NOTIFICATION_APP_NOTIFICATION) - .toIntent()); + final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); + intent.putExtra(Settings.EXTRA_APP_PACKAGE, mAppRow.pkg); + intent.putExtra(Settings.EXTRA_APP_UID, mAppRow.uid); + preference.setIntent(intent); } } @Override public CharSequence getSummary() { - boolean canBubble = false; - if (mAppRow != null) { - if (mChannel != null) { - canBubble |= mChannel.canBubble() && isGloballyEnabled(); - } else { - canBubble |= mAppRow.allowBubbles && isGloballyEnabled(); - } + if (mAppRow == null) { + return null; + } + int backEndPref = mAppRow.bubblePreference; + Resources res = mContext.getResources(); + if (backEndPref == BUBBLE_PREFERENCE_NONE || !isGloballyEnabled()) { + return res.getString(R.string.bubble_app_setting_none); + } else if (backEndPref == BUBBLE_PREFERENCE_ALL) { + return res.getString(R.string.bubble_app_setting_all); + } else { + return res.getString(R.string.bubble_app_setting_selected); } - return mContext.getString(canBubble ? R.string.switch_on_text : R.string.switch_off_text); } private boolean isGloballyEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), - NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON; + NOTIFICATION_BUBBLES, ON) == ON; } } diff --git a/src/com/android/settings/notification/app/BubbleWarningDialogFragment.java b/src/com/android/settings/notification/app/BubbleWarningDialogFragment.java index d3aa7585260..7d5b24a3050 100644 --- a/src/com/android/settings/notification/app/BubbleWarningDialogFragment.java +++ b/src/com/android/settings/notification/app/BubbleWarningDialogFragment.java @@ -27,6 +27,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment; public class BubbleWarningDialogFragment extends InstrumentedDialogFragment { static final String KEY_PKG = "p"; static final String KEY_UID = "u"; + static final String KEY_SELECTED_PREFERENCE = "pref"; @Override @@ -34,10 +35,11 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment { return SettingsEnums.DIALOG_APP_BUBBLE_SETTINGS; } - public BubbleWarningDialogFragment setPkgInfo(String pkg, int uid) { + public BubbleWarningDialogFragment setPkgPrefInfo(String pkg, int uid, int preference) { Bundle args = new Bundle(); args.putString(KEY_PKG, pkg); args.putInt(KEY_UID, uid); + args.putInt(KEY_SELECTED_PREFERENCE, preference); setArguments(args); return this; } @@ -48,6 +50,7 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment { final Bundle args = getArguments(); final String pkg = args.getString(KEY_PKG); final int uid = args.getInt(KEY_UID); + final int pref = args.getInt(KEY_SELECTED_PREFERENCE); final String title = getResources().getString(R.string.bubbles_feature_disabled_dialog_title); @@ -60,7 +63,7 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment { .setPositiveButton(R.string.bubbles_feature_disabled_button_approve, (dialog, id) -> BubblePreferenceController.applyBubblesApproval( - getContext(), pkg, uid)) + getContext(), pkg, uid, pref)) .setNegativeButton(R.string.bubbles_feature_disabled_button_cancel, (dialog, id) -> BubblePreferenceController.revertBubblesApproval( diff --git a/src/com/android/settings/slices/SlicesIndexer.java b/src/com/android/settings/slices/SlicesIndexer.java index 3a68a322853..e527fd657ab 100644 --- a/src/com/android/settings/slices/SlicesIndexer.java +++ b/src/com/android/settings/slices/SlicesIndexer.java @@ -104,7 +104,10 @@ class SlicesIndexer implements Runnable { values.put(IndexColumns.SLICE_URI, dataRow.getUri().toSafeString()); values.put(IndexColumns.TITLE, dataRow.getTitle()); values.put(IndexColumns.SUMMARY, dataRow.getSummary()); - values.put(IndexColumns.SCREENTITLE, dataRow.getScreenTitle().toString()); + final CharSequence screenTitle = dataRow.getScreenTitle(); + if (screenTitle != null) { + values.put(IndexColumns.SCREENTITLE, screenTitle.toString()); + } values.put(IndexColumns.KEYWORDS, dataRow.getKeywords()); values.put(IndexColumns.ICON_RESOURCE, dataRow.getIconResource()); values.put(IndexColumns.FRAGMENT, dataRow.getFragmentClassName()); diff --git a/tests/perftests/Android.bp b/tests/perftests/Android.bp index 1ba5d9c432a..7708bbe2569 100644 --- a/tests/perftests/Android.bp +++ b/tests/perftests/Android.bp @@ -19,4 +19,4 @@ android_test { test_suites: ["device-tests"], instrumentation_for: "Settings", -} +} \ No newline at end of file diff --git a/tests/perftests/AndroidManifest.xml b/tests/perftests/AndroidManifest.xml index 4ce6a54048c..972467f5fe6 100644 --- a/tests/perftests/AndroidManifest.xml +++ b/tests/perftests/AndroidManifest.xml @@ -15,15 +15,15 @@ --> + package="com.android.settings.tests.perf"> - + + android:targetPackage="com.android.settings.tests.perf" + android:label="Settings Performance Test Cases"> diff --git a/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java b/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java index 5140be2db91..f55598cb998 100644 --- a/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java +++ b/tests/perftests/src/com/android/settings/tests/perf/LaunchSettingsTest.java @@ -15,8 +15,15 @@ */ package com.android.settings.tests.perf; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static junit.framework.TestCase.fail; + import android.app.Instrumentation; import android.os.Bundle; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.Until; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -26,23 +33,119 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + @RunWith(AndroidJUnit4.class) public class LaunchSettingsTest { + private static class Page { + String action; + String displayName; + String title; + + Page(String action, String displayName, String title) { + this.action = action; + this.displayName = displayName; + this.title = title; + } + } + + private static final int TIME_OUT = 5000; + private static final int TEST_TIME = 10; + private static final Pattern PATTERN = Pattern.compile("TotalTime:\\s[0-9]*"); + private static final Page[] PAGES; + + static { + PAGES = new Page[]{ + new Page("android.settings.SETTINGS", "Search settings", "Settings"), + new Page("android.settings.WIFI_SETTINGS", "Use Wi‑Fi", "Wi-Fi"), + new Page("android.settings.BLUETOOTH_SETTINGS", "Connected devices", "BlueTooth"), + new Page("android.settings.APPLICATION_SETTINGS", "App info", "Application"), + new Page("android.intent.action.POWER_USAGE_SUMMARY", "Battery", "Battery") + }; + } + + private Bundle mBundle; + private UiDevice mDevice; + private Instrumentation mInstrumentation; + private Map> mResult; @Before public void setUp() throws Exception { + mBundle = new Bundle(); + mDevice = UiDevice.getInstance(getInstrumentation()); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mResult = new LinkedHashMap<>(); + mDevice.pressHome(); + mDevice.waitForIdle(TIME_OUT); + + for (Page page : PAGES) { + mResult.put(page.title, new ArrayList()); + } } @After public void tearDown() throws Exception { + putResultToBundle(); + mInstrumentation.sendStatus(0, mBundle); } @Test - public void testReportMetrics() throws Exception { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - final Bundle result = new Bundle(); - result.putString("LaunchSettingsTest_metric_key1", "1000"); - result.putString("LaunchSettingsTest_metric_key2", "5000"); - instrumentation.sendStatus(0, result); + public void settingsPerformanceTest() throws Exception { + for (int i = 0; i < TEST_TIME; i++) { + for (Page page : PAGES) { + executePreformanceTest(page.action, page.displayName, page.title); + } + } } -} + + private void executePreformanceTest(String action, String displayName, String title) + throws Exception { + final String mString = mDevice.executeShellCommand("am start -W -a" + action); + mDevice.wait(Until.findObject(By.text(displayName)), TIME_OUT); + handleLaunchResult(title, mString); + closeApp(); + mDevice.waitForIdle(TIME_OUT); + } + + private void handleLaunchResult(String title, String s) { + Matcher mMatcher = PATTERN.matcher(s); + if (mMatcher.find()) { + mResult.get(title).add(Integer.valueOf(mMatcher.group().split("\\s")[1])); + } else { + fail("Some pages can't be found"); + } + } + + private void closeApp() throws Exception { + mDevice.executeShellCommand("am force-stop com.android.settings"); + Thread.sleep(1000); + } + + private void putResultToBundle() { + for (String string : mResult.keySet()) { + mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "max"), + getMax(mResult.get(string))); + mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "min"), + getMin(mResult.get(string))); + mBundle.putString(String.format("LaunchSettingsTest_%s_%s", string, "avg"), + getAvg(mResult.get(string))); + } + } + + private String getMax(ArrayList launchResult) { + return String.format("%s", launchResult.isEmpty() ? "null" : Collections.max(launchResult)); + } + + private String getMin(ArrayList launchResult) { + return String.format("%s", launchResult.isEmpty() ? "null" : Collections.min(launchResult)); + } + + private String getAvg(ArrayList launchResult) { + return String.valueOf((int) launchResult.stream().mapToInt(i -> i).average().orElse(0)); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/AirplaneModeEnablerTest.java b/tests/robotests/src/com/android/settings/AirplaneModeEnablerTest.java index 24abac9f258..6c5b9f2323f 100644 --- a/tests/robotests/src/com/android/settings/AirplaneModeEnablerTest.java +++ b/tests/robotests/src/com/android/settings/AirplaneModeEnablerTest.java @@ -53,7 +53,7 @@ public final class AirplaneModeEnablerTest { @Test public void onRadioPowerStateChanged_beenInvoke_invokeOnAirplaneModeChanged() { - mAirplaneModeEnabler.resume(); + mAirplaneModeEnabler.start(); ShadowSettings.setAirplaneMode(true); diff --git a/tests/robotests/src/com/android/settings/development/BubbleGlobalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BubbleGlobalPreferenceControllerTest.java deleted file mode 100644 index 9e52a8829c9..00000000000 --- a/tests/robotests/src/com/android/settings/development/BubbleGlobalPreferenceControllerTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 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.development; - -import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; - -import static com.android.settings.development.BubbleGlobalPreferenceController.OFF; -import static com.android.settings.development.BubbleGlobalPreferenceController.ON; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.provider.Settings; - -import androidx.preference.PreferenceScreen; -import androidx.preference.SwitchPreference; - -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 BubbleGlobalPreferenceControllerTest { - private Context mContext; - - @Mock - private SwitchPreference mPreference; - @Mock - private PreferenceScreen mPreferenceScreen; - - private BubbleGlobalPreferenceController mController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mController = new BubbleGlobalPreferenceController(mContext); - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) - .thenReturn(mPreference); - mController.displayPreference(mPreferenceScreen); - } - - @Test - public void onPreferenceChange_settingEnabled_allowBubbles_shouldBeOn() { - mController.onPreferenceChange(mPreference, true /* new value */); - - assertThat(isSettingEnabled()).isTrue(); - } - - @Test - public void onPreferenceChange_settingDisabled_allowBubbles_shouldBeOff() { - mController.onPreferenceChange(mPreference, false /* new value */); - - assertThat(isSettingEnabled()).isFalse(); - } - - @Test - public void updateState_settingEnabled_preferenceShouldBeChecked() { - Settings.Global.putInt(mContext.getContentResolver(), - NOTIFICATION_BUBBLES, 1 /* enabled */); - mController.updateState(mPreference); - - verify(mPreference).setChecked(true); - } - - @Test - public void updateState_settingReset_defaultDisabled_preferenceShouldNotBeChecked() { - Settings.Global.putInt(mContext.getContentResolver(), - NOTIFICATION_BUBBLES, 0 /* enabled */); - mController.updateState(mPreference); - - verify(mPreference).setChecked(false); - } - - @Test - public void onDeveloperOptionsSwitchDisabled_shouldDisable() { - mController.onDeveloperOptionsSwitchDisabled(); - - verify(mPreference).setChecked(false); - verify(mPreference).setEnabled(false); - - assertThat(isSettingEnabled()).isFalse(); - } - - private boolean isSettingEnabled() { - return Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, - OFF /* default off */) == ON; - } - -} diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java index 54c36833d45..cb68e2a6aa9 100644 --- a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java @@ -102,8 +102,8 @@ public class AirplaneModePreferenceControllerTest { mController.displayPreference(mScreen); // This should not crash - mController.onResume(); - mController.onPause(); + mController.onStart(); + mController.onStop(); } @Test @@ -115,8 +115,8 @@ public class AirplaneModePreferenceControllerTest { mController.displayPreference(mScreen); // This should not crash - mController.onResume(); - mController.onPause(); + mController.onStart(); + mController.onStop(); } @Test @@ -147,7 +147,7 @@ public class AirplaneModePreferenceControllerTest { Settings.Global.putInt(mResolver, Settings.Global.AIRPLANE_MODE_ON, ON); mController.displayPreference(mScreen); - mController.onResume(); + mController.onStart(); assertThat(mController.isChecked()).isTrue(); @@ -161,7 +161,7 @@ public class AirplaneModePreferenceControllerTest { Settings.Global.putInt(mResolver, Settings.Global.AIRPLANE_MODE_ON, OFF); mController.displayPreference(mScreen); - mController.onResume(); + mController.onStop(); assertThat(mPreference.isChecked()).isFalse(); diff --git a/tests/robotests/src/com/android/settings/network/PreferredNetworkModeContentObserverTest.java b/tests/robotests/src/com/android/settings/network/PreferredNetworkModeContentObserverTest.java new file mode 100644 index 00000000000..657b8b3dc9d --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/PreferredNetworkModeContentObserverTest.java @@ -0,0 +1,82 @@ +/* + * 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.network; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +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 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 PreferredNetworkModeContentObserverTest { + + private static final int SUB_ID = 1; + + @Mock + private ContentResolver mResolver; + @Mock + private Context mContext; + @Mock + private PreferredNetworkModeContentObserver.OnPreferredNetworkModeChangedListener mListener; + + private PreferredNetworkModeContentObserver mPreferredNetworkModeContentObserver; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getContentResolver()).thenReturn(mResolver); + mPreferredNetworkModeContentObserver = + spy(new PreferredNetworkModeContentObserver(null)); + } + + @Test + public void onChange_shouldCallListener() { + mPreferredNetworkModeContentObserver.mListener = mListener; + mPreferredNetworkModeContentObserver.onChange(true); + + verify(mListener).onPreferredNetworkModeChanged(); + } + + @Test + public void register_shouldRegisterContentObserver() { + mPreferredNetworkModeContentObserver.register(mContext, SUB_ID); + + verify(mResolver).registerContentObserver( + Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + SUB_ID), false, + mPreferredNetworkModeContentObserver); + } + + @Test + public void unregister_shouldUnregisterContentObserver() { + mPreferredNetworkModeContentObserver.unregister(mContext); + + verify(mResolver).unregisterContentObserver(mPreferredNetworkModeContentObserver); + } + +} diff --git a/tests/robotests/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java index 0ae2dc6ec6e..d1a794d079d 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java @@ -30,9 +30,11 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.SwitchPreference; import com.android.settings.R; +import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; import org.junit.Test; @@ -62,12 +64,16 @@ public class AutoSelectPreferenceControllerTest { private AutoSelectPreferenceController mController; private SwitchPreference mSwitchPreference; private Context mContext; + private LifecycleOwner mLifecycleOwner; + private Lifecycle mLifecycle; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn( @@ -83,7 +89,7 @@ public class AutoSelectPreferenceControllerTest { mController = new AutoSelectPreferenceController(mContext, "auto_select"); mController.mProgressDialog = mProgressDialog; mController.mSwitchPreference = mSwitchPreference; - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); } @Test @@ -125,6 +131,6 @@ public class AutoSelectPreferenceControllerTest { when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(null); // Should not crash - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); } } diff --git a/tests/robotests/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceControllerTest.java index 5c182208d79..60de5aa146c 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceControllerTest.java @@ -17,7 +17,7 @@ package com.android.settings.network.telephony.gsm; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doReturn; + import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -29,11 +29,11 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import com.android.settings.R; -import com.android.settings.network.telephony.MobileNetworkUtils; -import java.util.Arrays; +import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; import org.junit.Test; @@ -43,6 +43,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Arrays; + @RunWith(RobolectricTestRunner.class) public class OpenNetworkSelectPagePreferenceControllerTest { private static final int SUB_ID = 2; @@ -63,12 +65,16 @@ public class OpenNetworkSelectPagePreferenceControllerTest { private OpenNetworkSelectPagePreferenceController mController; private Preference mPreference; private Context mContext; + private LifecycleOwner mLifecycleOwner; + private Lifecycle mLifecycle; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn( @@ -92,7 +98,7 @@ public class OpenNetworkSelectPagePreferenceControllerTest { mPreference = new Preference(mContext); mController = new OpenNetworkSelectPagePreferenceController(mContext, "open_network_select"); - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java new file mode 100644 index 00000000000..b2cf55b97b2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java @@ -0,0 +1,138 @@ +/* + * 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; + +import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF; +import static com.android.settings.notification.BadgingNotificationPreferenceController.ON; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.TwoStatePreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class BubbleNotificationPreferenceControllerTest { + + private Context mContext; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + + private BubbleNotificationPreferenceController mController; + private Preference mPreference; + + private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = new BubbleNotificationPreferenceController(mContext, + KEY_NOTIFICATION_BUBBLES); + mPreference = new Preference(RuntimeEnvironment.application); + mPreference.setKey(mController.getPreferenceKey()); + when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference); + } + + @Test + public void getAvilabilityStatus_returnsAvailable() { + assertEquals(AVAILABLE, mController.getAvailabilityStatus()); + } + + @Test + public void updateState_settingIsOn_preferenceSetChecked() { + final TwoStatePreference preference = mock(TwoStatePreference.class); + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); + + mController.updateState(preference); + + verify(preference).setChecked(true); + } + + @Test + public void updateState_settingIsOff_preferenceSetUnchecked() { + final TwoStatePreference preference = mock(TwoStatePreference.class); + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); + assertThat(Settings.Global.getInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, ON)).isEqualTo(OFF); + + mController.updateState(preference); + + verify(preference).setChecked(false); + } + + @Test + public void isChecked_settingIsOff_shouldReturnFalse() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void isChecked_settingIsOn_shouldReturnTrue() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void setChecked_setFalse_disablesSetting() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); + + mController.setChecked(false); + int updatedValue = Settings.Global.getInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, -1); + + assertThat(updatedValue).isEqualTo(OFF); + } + + @Test + public void setChecked_setTrue_enablesSetting() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); + + mController.setChecked(true); + int updatedValue = Settings.Global.getInt(mContext.getContentResolver(), + NOTIFICATION_BUBBLES, -1); + + assertThat(updatedValue).isEqualTo(ON); + } + + @Test + public void isSliceable_returnsFalse() { + assertThat(mController.isSliceable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java new file mode 100644 index 00000000000..b5f505b6e82 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java @@ -0,0 +1,71 @@ +/* + * 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; + +import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; + +import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF; +import static com.android.settings.notification.BadgingNotificationPreferenceController.ON; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class BubbleSummaryNotificationPreferenceControllerTest { + + private Context mContext; + + private BubbleSummaryNotificationPreferenceController mController; + private Preference mPreference; + + private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mController = new BubbleSummaryNotificationPreferenceController(mContext, + KEY_NOTIFICATION_BUBBLES); + mPreference = new Preference(RuntimeEnvironment.application); + } + + @Test + public void getSummary_NOTIFICATION_BUBBLESIsOff_returnOffString() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); + + assertThat(mController.getSummary()).isEqualTo("Off"); + } + + @Test + public void getSummary_NOTIFICATION_BUBBLESIsOff_returnOnString() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); + + String onString = mContext.getString(R.string.notifications_bubble_setting_on_summary); + assertThat(mController.getSummary()).isEqualTo(onString); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java index c2c45cb1707..0cf6dc67829 100644 --- a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java @@ -17,6 +17,9 @@ package com.android.settings.notification.app; import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -25,8 +28,8 @@ import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_OFF; import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -44,6 +47,11 @@ import android.content.Context; import android.os.UserManager; import android.provider.Settings; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.settings.notification.NotificationBackend; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedSwitchPreference; @@ -58,11 +66,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - @RunWith(RobolectricTestRunner.class) public class BubblePreferenceControllerTest { @@ -125,7 +128,7 @@ public class BubblePreferenceControllerTest { public void testIsAvailable_channel_yesIfAppOff() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = false; + appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; NotificationChannel channel = mock(NotificationChannel.class); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); mController.onResume(appRow, channel, null, null, null, null); @@ -177,7 +180,7 @@ public class BubblePreferenceControllerTest { @Test public void testIsAvailable_defaultChannel() { NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; NotificationChannel channel = mock(NotificationChannel.class); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID); @@ -190,7 +193,7 @@ public class BubblePreferenceControllerTest { @Test public void testIsAvailable_channel() { NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; NotificationChannel channel = mock(NotificationChannel.class); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); mController.onResume(appRow, channel, null, null, null, null); @@ -213,7 +216,20 @@ public class BubblePreferenceControllerTest { } @Test - public void testUpdateState_channelNotBlockable() { + public void testUpdateState_app_disabledByAdmin() { + NotificationChannel channel = mock(NotificationChannel.class); + when(channel.getId()).thenReturn("something"); + mAppPageController.onResume(new NotificationBackend.AppRow(), channel, null, + null, null, mock(RestrictedLockUtils.EnforcedAdmin.class)); + + BubblePreference pref = new BubblePreference(mContext); + mAppPageController.updateState(pref); + + assertFalse(pref.isEnabled()); + } + + @Test + public void testUpdateState_channel_channelNotBlockable() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationChannel channel = mock(NotificationChannel.class); @@ -251,21 +267,24 @@ public class BubblePreferenceControllerTest { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); appRow.label = "App!"; - appRow.allowBubbles = true; - mController.onResume(appRow, null, null, null, null, null); + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; + mAppPageController.onResume(appRow, null, null, null, null, null); - RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); - mController.updateState(pref); - assertTrue(pref.isChecked()); + BubblePreference pref = new BubblePreference(mContext); + mAppPageController.updateState(pref); + assertEquals(BUBBLE_PREFERENCE_ALL, pref.getSelectedPreference()); - appRow.allowBubbles = false; - mController.onResume(appRow, null, null, null, null, null); + appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; + mAppPageController.onResume(appRow, null, null, null, null, null); - mController.updateState(pref); - assertFalse(pref.isChecked()); + mAppPageController.updateState(pref); + assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference()); - assertNotNull(pref.getSummary()); - assertTrue(pref.getSummary().toString().contains(appRow.label)); + appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; + mAppPageController.onResume(appRow, null, null, null, null, null); + + mAppPageController.updateState(pref); + assertEquals(BUBBLE_PREFERENCE_SELECTED, pref.getSelectedPreference()); } @Test @@ -274,22 +293,21 @@ public class BubblePreferenceControllerTest { NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); appRow.label = "App!"; - appRow.allowBubbles = true; - mController.onResume(appRow, null, null, null, null, null); + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; + mAppPageController.onResume(appRow, null, null, null, null, null); - RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); - mController.updateState(pref); - assertFalse(pref.isChecked()); + BubblePreference pref = new BubblePreference(mContext); + mAppPageController.updateState(pref); + assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference()); } @Test public void testOnPreferenceChange_on_channel() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; + appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; NotificationChannel channel = new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW); - channel.setAllowBubbles(false); mController.onResume(appRow, channel, null, null, null, null); RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); @@ -306,10 +324,9 @@ public class BubblePreferenceControllerTest { public void testOnPreferenceChange_off_channel() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; + appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; NotificationChannel channel = new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH); - channel.setAllowBubbles(true); mController.onResume(appRow, channel, null, null, null, null); RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); @@ -322,59 +339,78 @@ public class BubblePreferenceControllerTest { assertFalse(channel.canBubble()); } + @Test - public void testOnPreferenceChange_on_app() { + public void testOnPreferenceChange_app_all() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = false; - mController.onResume(appRow, null, null, null, null, null); + appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; + mAppPageController.onResume(appRow, null, null, null, null, null); - RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); + BubblePreference pref = new BubblePreference(mContext); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); - mController.displayPreference(mScreen); - mController.updateState(pref); + mAppPageController.displayPreference(mScreen); + mAppPageController.updateState(pref); - mController.onPreferenceChange(pref, true); + mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL); - assertTrue(appRow.allowBubbles); - verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(true)); + assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_ALL); + verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL)); } @Test - public void testOnPreferenceChange_off_app() { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); - NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; - mController.onResume(appRow, null, null, null, null, null); - - RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); - when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); - mController.displayPreference(mScreen); - mController.updateState(pref); - - mController.onPreferenceChange(pref, false); - - assertFalse(appRow.allowBubbles); - verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(false)); - } - - @Test - public void testOnPreferenceChange_on_app_offGlobally() { + public void testOnPreferenceChange_app_all_offGlobally() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = false; - mController.onResume(appRow, null, null, null, null, null); + appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; + mAppPageController.onResume(appRow, null, null, null, null, null); - RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); + BubblePreference pref = new BubblePreference(mContext); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); - mController.displayPreference(mScreen); - mController.updateState(pref); + mAppPageController.displayPreference(mScreen); + mAppPageController.updateState(pref); - mController.onPreferenceChange(pref, true); + mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL); - assertFalse(appRow.allowBubbles); - verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(true)); + assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_NONE); + verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL)); verify(mFragmentManager, times(1)).beginTransaction(); } + + @Test + public void testOnPreferenceChange_app_selected() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; + mAppPageController.onResume(appRow, null, null, null, null, null); + + BubblePreference pref = new BubblePreference(mContext); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); + mAppPageController.displayPreference(mScreen); + mAppPageController.updateState(pref); + + mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_NONE); + + assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference); + verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE)); + } + + @Test + public void testOnPreferenceChange_app_none() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; + mAppPageController.onResume(appRow, null, null, null, null, null); + + BubblePreference pref = new BubblePreference(mContext); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref); + mAppPageController.displayPreference(mScreen); + mAppPageController.updateState(pref); + + mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_NONE); + + assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference); + verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE)); + } } diff --git a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java index 80abfbb5f20..9d664ac02c8 100644 --- a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java @@ -17,28 +17,31 @@ package com.android.settings.notification.app; import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; -import static com.android.settings.notification.app.BubbleSummaryPreferenceController.SYSTEM_WIDE_OFF; -import static com.android.settings.notification.app.BubbleSummaryPreferenceController.SYSTEM_WIDE_ON; +import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_OFF; +import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.NotificationChannel; import android.content.Context; import android.provider.Settings; +import androidx.preference.Preference; + +import com.android.settings.R; import com.android.settings.notification.NotificationBackend; import org.junit.Before; @@ -50,8 +53,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; -import androidx.preference.Preference; - @RunWith(RobolectricTestRunner.class) public class BubbleSummaryPreferenceControllerTest { @@ -70,13 +71,13 @@ public class BubbleSummaryPreferenceControllerTest { } @Test - public void testNoCrashIfNoOnResume() { + public void isAvailable_noOnResume_shouldNotCrash() { mController.isAvailable(); mController.updateState(mock(Preference.class)); } @Test - public void testIsAvailable_notIfAppBlocked() { + public void isAvailable_appBlocked_shouldReturnFalse() { NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); appRow.banned = true; mController.onResume(appRow, mock(NotificationChannel.class), null, null, null, null); @@ -84,53 +85,52 @@ public class BubbleSummaryPreferenceControllerTest { } @Test - public void testIsAvailable_notIfOffGlobally() { - NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - NotificationChannel channel = mock(NotificationChannel.class); - when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); - mController.onResume(appRow, channel, null, null, null, null); - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, - SYSTEM_WIDE_OFF); - - assertFalse(mController.isAvailable()); - } - - @Test - public void testIsAvailable_app() { + public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOn_shouldReturnTrue() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); mController.onResume(appRow, null, null, null, null, null); - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); assertTrue(mController.isAvailable()); } @Test - public void testIsNotAvailable_app_globalOff() { - NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - mController.onResume(appRow, null, null, null, null, null); + public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOff_shouldReturnFalse() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + mController.onResume(appRow, null, null, null, null, null); assertFalse(mController.isAvailable()); } @Test - public void testIsAvailable_defaultChannel() { + public void isAvailable_nonNullChannelNOTIFICATION_BUBBLESisOff_shouldReturnFalse() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, + SYSTEM_WIDE_OFF); + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + NotificationChannel channel = mock(NotificationChannel.class); + when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); + mController.onResume(appRow, channel, null, null, null, null); + + assertFalse(mController.isAvailable()); + } + + @Test + public void isAvailable_defaultChannelNOTIFICATION_BUBBLESisOn_shouldReturnTrue() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; NotificationChannel channel = mock(NotificationChannel.class); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID); mController.onResume(appRow, channel, null, null, null, null); - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); assertTrue(mController.isAvailable()); } @Test - public void testUpdateState() { + public void updateState_setsIntent() { NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; mController.onResume(appRow, null, null, null, null, null); Preference pref = new Preference(mContext); @@ -139,22 +139,53 @@ public class BubbleSummaryPreferenceControllerTest { } @Test - public void testGetSummary() { - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); - NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); - appRow.allowBubbles = true; - mController.onResume(appRow, null, null, null, null, null); - - assertEquals("On", mController.getSummary()); - + public void getSummary_NOTIFICATION_BUBBLESIsOff_returnsNoneString() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); - assertEquals("Off", mController.getSummary()); - Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); - appRow.allowBubbles = false; + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); mController.onResume(appRow, null, null, null, null, null); - assertEquals("Off", mController.getSummary()); + String noneString = mContext.getString(R.string.bubble_app_setting_none); + assertEquals(noneString, mController.getSummary()); + } + + @Test + public void getSummary_BUBBLE_PREFERENCE_NONEisSelected_returnsNoneString() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, + SYSTEM_WIDE_ON); + + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; + mController.onResume(appRow, null, null, null, null, null); + + String noneString = mContext.getString(R.string.bubble_app_setting_none); + assertEquals(noneString, mController.getSummary()); + } + + @Test + public void getSummary_BUBBLE_PREFERENCE_ALLisSelected_returnsAllString() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, + SYSTEM_WIDE_ON); + + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; + mController.onResume(appRow, null, null, null, null, null); + + String allString = mContext.getString(R.string.bubble_app_setting_all); + assertEquals(allString, mController.getSummary()); + } + + @Test + public void getSummary_BUBBLE_PREFERENCE_SELECTEDisSelected_returnsSelectedString() { + Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, + SYSTEM_WIDE_ON); + + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; + mController.onResume(appRow, null, null, null, null, null); + + String selectedString = mContext.getString(R.string.bubble_app_setting_selected); + assertEquals(selectedString, mController.getSummary()); } }