From f4f68c41179cedf22300ca07be06e116fd649980 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 11 May 2018 23:23:55 +0000 Subject: [PATCH 1/9] Remove developer menu: AVDTP delay reports/inband ringing Bluetooth AVDTP delay reports and inband ringing are used by default. Developer option menu no longer used. Bug: 79610154 Test: robolectric Change-Id: I67fe10808151f101fc7e891f75953f8539255026 Merged-In: I67fe10808151f101fc7e891f75953f8539255026 --- res/values/strings.xml | 10 -- res/xml/development_settings.xml | 10 -- ...toothDelayReportsPreferenceController.java | 69 ---------- ...oothInbandRingingPreferenceController.java | 78 ------------ .../DevelopmentSettingsDashboardFragment.java | 2 - ...hDelayReportsPreferenceControllerTest.java | 110 ---------------- ...InbandRingingPreferenceControllerTest.java | 120 ------------------ ...elopmentSettingsDashboardFragmentTest.java | 10 -- 8 files changed, 409 deletions(-) delete mode 100644 src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java delete mode 100644 src/com/android/settings/development/BluetoothInbandRingingPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java delete mode 100644 tests/robotests/src/com/android/settings/development/BluetoothInbandRingingPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 8347f9956c9..0938b0ba91a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -402,11 +402,6 @@ Allow your phone to communicate with nearby Bluetooth devices - - Disable in-band ringing - - Don’t play custom phone ringtones on Bluetooth headsets - Available media devices @@ -1691,15 +1686,10 @@ Remember settings - Maximum connected Bluetooth audio devices Select maximum number of connected Bluetooth audio devices - - Disable Bluetooth AVDTP delay reports - - Disallow receiving Bluetooth AVDTP delay reports Cast diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index c491980153c..3836b43930b 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -254,16 +254,6 @@ android:title="@string/bluetooth_disable_absolute_volume" android:summary="@string/bluetooth_disable_absolute_volume_summary" /> - - - - Date: Fri, 11 May 2018 14:03:25 -0400 Subject: [PATCH 2/9] DO NOT MERGE Always show custom zen vis eff option + dnd string edits Change-Id: I2fcbe6c527b40610f997efacb08049c07b3a6dd7 Bug: 79538038 Bug: 78447976 Test: ZenModeViseffectsCustomPreferenceControllerTest --- res/values/strings.xml | 17 ++++- res/xml/zen_mode_calls_settings.xml | 4 +- .../zen_mode_msg_event_reminder_settings.xml | 6 +- .../ZenModeRestrictNotificationsSettings.java | 63 ++----------------- .../notification/ZenModeSettings.java | 24 ++++--- ...eVisEffectsCustomPreferenceController.java | 11 +--- .../notification/ZenModeSettingsTest.java | 4 +- ...EffectsCustomPreferenceControllerTest.java | 13 +--- 8 files changed, 45 insertions(+), 97 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 8bdfb5f857a..3319555f206 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7928,6 +7928,9 @@ Calls + + Allow calls + When Do Not Disturb is on, incoming calls are blocked. You can adjust settings to allow your friends, family, or other contacts to reach you. @@ -7943,6 +7946,9 @@ Messages + + Allow messages + Messages @@ -7982,9 +7988,15 @@ Reminders + + Allow reminders + Events + + Allow events + anyone @@ -7997,8 +8009,11 @@ Repeat callers + + Allow repeat callers + - From %1$s only + From %1$s From %1$s and %2$s diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml index 62d9ef4490d..1a6655ffde6 100644 --- a/res/xml/zen_mode_calls_settings.xml +++ b/res/xml/zen_mode_calls_settings.xml @@ -25,7 +25,7 @@ @@ -36,7 +36,7 @@ + android:title="@string/zen_mode_repeat_callers_title" /> diff --git a/res/xml/zen_mode_msg_event_reminder_settings.xml b/res/xml/zen_mode_msg_event_reminder_settings.xml index 2f065a6e881..9bee9e94483 100644 --- a/res/xml/zen_mode_msg_event_reminder_settings.xml +++ b/res/xml/zen_mode_msg_event_reminder_settings.xml @@ -25,7 +25,7 @@ @@ -36,12 +36,12 @@ + android:title="@string/zen_mode_reminders_title"/> + android:title="@string/zen_mode_events_title"/> diff --git a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java index 14b82f52836..8d0cd0eb5c8 100644 --- a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java +++ b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java @@ -16,22 +16,15 @@ package com.android.settings.notification; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ZEN_SHOW_CUSTOM; - import android.content.Context; import android.os.Bundle; import android.provider.SearchIndexableResource; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; -import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.FooterPreference; @@ -40,67 +33,21 @@ import java.util.List; public class ZenModeRestrictNotificationsSettings extends ZenModeSettingsBase implements Indexable { - protected static final int APP_MENU_SHOW_CUSTOM = 1; - protected boolean mShowMenuSelected; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - menu.add(0, APP_MENU_SHOW_CUSTOM, 0, R.string.zen_mode_restrict_notifications_enable_custom) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - if (menuItem.getItemId() == APP_MENU_SHOW_CUSTOM) { - final FeatureFactory featureFactory = FeatureFactory.getFactory(mContext); - MetricsFeatureProvider metrics = featureFactory.getMetricsFeatureProvider(); - - mShowMenuSelected = !mShowMenuSelected; - - ZenModeVisEffectsCustomPreferenceController custom = - use(ZenModeVisEffectsCustomPreferenceController.class); - custom.setShownByMenu(mShowMenuSelected); - custom.displayPreference(getPreferenceScreen()); - - if (mShowMenuSelected) { - metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, true); - } else { - metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, false); - } - - return true; - } - return false; - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - if (mShowMenuSelected) { - menu.findItem(APP_MENU_SHOW_CUSTOM) - .setTitle(R.string.zen_mode_restrict_notifications_disable_custom); - } else { - menu.findItem(APP_MENU_SHOW_CUSTOM) - .setTitle(R.string.zen_mode_restrict_notifications_enable_custom); - } - - if (mShowMenuSelected && use(ZenModeVisEffectsCustomPreferenceController.class) - .areCustomOptionsSelected()) { - menu.findItem(APP_MENU_SHOW_CUSTOM).setEnabled(false); - } else { - menu.findItem(APP_MENU_SHOW_CUSTOM).setEnabled(true); - } - } - @Override protected List createPreferenceControllers(Context context) { return buildPreferenceControllers(context, getLifecycle()); } + @Override + public int getHelpResource() { + return R.string.help_uri_interruptions; + } + private static List buildPreferenceControllers(Context context, Lifecycle lifecycle) { List controllers = new ArrayList<>(); diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java index ec0f6192d94..62b3fe55dd6 100644 --- a/src/com/android/settings/notification/ZenModeSettings.java +++ b/src/com/android/settings/notification/ZenModeSettings.java @@ -30,6 +30,7 @@ import android.app.FragmentManager; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; +import android.icu.text.ListFormatter; import android.provider.SearchIndexableResource; import android.provider.Settings; import android.service.notification.ZenModeConfig; @@ -166,17 +167,20 @@ public class ZenModeSettings extends ZenModeSettingsBase { return mContext.getString(R.string.join_two_items, enabledCategories.get(0), enabledCategories.get(1).toLowerCase()); } else if (numCategories == 3){ - String secondaryText = mContext.getString(R.string.join_two_unrelated_items, - enabledCategories.get(0), enabledCategories.get(1).toLowerCase()); - return mContext.getString(R.string.join_many_items_last, secondaryText, - enabledCategories.get(2).toLowerCase()); + final List summaries = new ArrayList<>(); + summaries.add(enabledCategories.get(0)); + summaries.add(enabledCategories.get(1).toLowerCase()); + summaries.add(enabledCategories.get(2).toLowerCase()); + + return ListFormatter.getInstance().format(summaries); } else { - String secondaryText = mContext.getString(R.string.join_many_items_middle, - enabledCategories.get(0), enabledCategories.get(1).toLowerCase()); - secondaryText = mContext.getString(R.string.join_many_items_middle, secondaryText, - enabledCategories.get(2).toLowerCase()); - return mContext.getString(R.string.join_many_items_last, secondaryText, - mContext.getString(R.string.zen_mode_other_options)); + final List summaries = new ArrayList<>(); + summaries.add(enabledCategories.get(0)); + summaries.add(enabledCategories.get(1).toLowerCase()); + summaries.add(enabledCategories.get(2).toLowerCase()); + summaries.add(mContext.getString(R.string.zen_mode_other_options)); + + return ListFormatter.getInstance().format(summaries); } } diff --git a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java index 83ab0377256..0a7f7269ca7 100644 --- a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java @@ -28,7 +28,6 @@ import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeVisEffectsCustomPreferenceController extends AbstractZenModePreferenceController { - protected boolean mShowMenuSelected; protected static final int INTERRUPTIVE_EFFECTS = Policy.SUPPRESSED_EFFECT_AMBIENT | Policy.SUPPRESSED_EFFECT_PEEK | Policy.SUPPRESSED_EFFECT_LIGHTS @@ -41,11 +40,7 @@ public class ZenModeVisEffectsCustomPreferenceController @Override public boolean isAvailable() { - if (mShowMenuSelected) { - return true; - } - - return areCustomOptionsSelected(); + return true; } @Override @@ -64,10 +59,6 @@ public class ZenModeVisEffectsCustomPreferenceController }); } - protected void setShownByMenu(boolean shown) { - mShowMenuSelected = shown; - } - protected boolean areCustomOptionsSelected() { boolean allEffectsSuppressed = Policy.areAllVisualEffectsSuppressed(mBackend.mPolicy.suppressedVisualEffects); diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java index 5b2782fb6b7..39e9271fa2f 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java @@ -134,13 +134,13 @@ public class ZenModeSettingsTest { public void testGetCallsSettingSummary_contacts() { Policy policy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS | Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_CONTACTS, 0, 0); - assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From contacts only"); + assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From contacts"); } @Test public void testGetCallsSettingSummary_repeatCallers() { Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0); - assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From repeat callers only"); + assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From repeat callers"); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java index 14de98cdf27..b1692bc7629 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java @@ -88,23 +88,14 @@ public class ZenModeVisEffectsCustomPreferenceControllerTest { } @Test - public void isAvailable_menuOff_noVisEffects() { + public void isAvailable_noVisEffects() { mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 0); - mController.mShowMenuSelected = false; - assertThat(mController.isAvailable()).isFalse(); - } - - @Test - public void isAvailable_menuOn_noVisEffects() { - mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 0); - mController.mShowMenuSelected = true; assertThat(mController.isAvailable()).isTrue(); } @Test - public void isAvailable_menuOn_visEffects() { + public void isAvailable_visEffects() { mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 1); - mController.mShowMenuSelected = false; assertThat(mController.isAvailable()).isTrue(); } From 2e76a9ba541ed07cb544fe4b3d8be59de7598266 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Mon, 14 May 2018 11:14:19 -0400 Subject: [PATCH 3/9] Protect against multiple starts of device index job Its rare, but can happen. Test: existing tests pass Change-Id: Ie7aaa47902741d5f7fdd954f35e8f13e9466e07c Fixes: 78898947 --- .../settings/search/DeviceIndexUpdateJobService.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/search/DeviceIndexUpdateJobService.java b/src/com/android/settings/search/DeviceIndexUpdateJobService.java index 12a9cf00fcb..510da3a6226 100644 --- a/src/com/android/settings/search/DeviceIndexUpdateJobService.java +++ b/src/com/android/settings/search/DeviceIndexUpdateJobService.java @@ -53,10 +53,12 @@ public class DeviceIndexUpdateJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { if (DEBUG) Log.d(TAG, "onStartJob"); - mRunningJob = true; - Thread thread = new Thread(() -> updateIndex(params)); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); + if (!mRunningJob) { + mRunningJob = true; + Thread thread = new Thread(() -> updateIndex(params)); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } return true; } From 6d2aaed8ae111db026bf9774a4062e22081db559 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Wed, 9 May 2018 17:27:17 -0700 Subject: [PATCH 4/9] Update the battery broadcast receiver. Add a type for battery receiver, then in callback client know for which reason it been invoked: 1. battery level change 2. battery saver state change 3. battery plug state change So in this CL, we won't update battery tip for battery level change, then battery tip won't be dismissed by itself. Also note in onResume() we will manually update battery tip. So if user stay in battery settings page and close the screen, once he opens it we will still force update everything. Fixes: 79171742 Test: RunSettingsRoboTests Change-Id: I997844216fd8267e545d74e0d434de9e338f76a1 Merged-In: I997844216fd8267e545d74e0d434de9e338f76a1 --- .../fuelgauge/BatteryBroadcastReceiver.java | 34 +++++++++--- .../fuelgauge/PowerUsageAdvanced.java | 7 ++- .../settings/fuelgauge/PowerUsageBase.java | 55 ++++++++++++------- .../settings/fuelgauge/PowerUsageSummary.java | 25 ++++----- .../BatteryBroadcastReceiverTest.java | 13 +++-- .../fuelgauge/PowerUsageAdvancedTest.java | 3 +- .../fuelgauge/PowerUsageBaseTest.java | 2 +- .../fuelgauge/PowerUsageSummaryTest.java | 26 +++++---- 8 files changed, 101 insertions(+), 64 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java index ca4742d1fcb..3075d9bf0ea 100644 --- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java @@ -21,9 +21,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.PowerManager; +import android.support.annotation.IntDef; import android.support.annotation.VisibleForTesting; import com.android.settings.Utils; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Use this broadcastReceiver to listen to the battery change, and it will invoke @@ -43,7 +48,19 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { * Battery saver(e.g. off->on) */ public interface OnBatteryChangedListener { - void onBatteryChanged(); + void onBatteryChanged(@BatteryUpdateType int type); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({BatteryUpdateType.MANUAL, + BatteryUpdateType.BATTERY_LEVEL, + BatteryUpdateType.BATTERY_SAVER, + BatteryUpdateType.BATTERY_STATUS}) + public @interface BatteryUpdateType { + int MANUAL = 0; + int BATTERY_LEVEL = 1; + int BATTERY_SAVER = 2; + int BATTERY_STATUS = 3; } @VisibleForTesting @@ -85,14 +102,17 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { final String batteryLevel = Utils.getBatteryPercentage(intent); final String batteryStatus = Utils.getBatteryStatus( mContext.getResources(), intent); - if (forceUpdate || !batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals( - mBatteryStatus)) { - mBatteryLevel = batteryLevel; - mBatteryStatus = batteryStatus; - mBatteryListener.onBatteryChanged(); + if (forceUpdate) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.MANUAL); + } else if(!batteryLevel.equals(mBatteryLevel)) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL); + } else if (!batteryStatus.equals(mBatteryStatus)) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); } + mBatteryLevel = batteryLevel; + mBatteryStatus = batteryStatus; } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - mBatteryListener.onBatteryChanged(); + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER); } } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java index 327a6c58bfa..64934308820 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java @@ -13,6 +13,8 @@ */ package com.android.settings.fuelgauge; +import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType; + import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -30,7 +32,6 @@ import com.android.settings.SettingsActivity; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.utils.StringUtil; import java.util.ArrayList; import java.util.Arrays; @@ -106,7 +107,7 @@ public class PowerUsageAdvanced extends PowerUsageBase { mMetricsFeatureProvider.action(getContext(), MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, mShowAllApps); - restartBatteryStatsLoader(); + restartBatteryStatsLoader(BatteryUpdateType.MANUAL); return true; default: return super.onOptionsItemSelected(item); @@ -138,7 +139,7 @@ public class PowerUsageAdvanced extends PowerUsageBase { } @Override - protected void refreshUi() { + protected void refreshUi(@BatteryUpdateType int refreshType) { final Context context = getContext(); if (context == null) { return; diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java index b811f208b84..58d9d0885d3 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageBase.java +++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java @@ -15,6 +15,8 @@ */ package com.android.settings.fuelgauge; +import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.*; + import android.app.Activity; import android.app.LoaderManager; import android.content.Context; @@ -26,18 +28,17 @@ import android.view.Menu; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.dashboard.DashboardFragment; -import com.android.settingslib.utils.AsyncLoader; /** * Common base class for things that need to show the battery usage graph. */ -public abstract class PowerUsageBase extends DashboardFragment - implements LoaderManager.LoaderCallbacks { +public abstract class PowerUsageBase extends DashboardFragment { // +1 to allow ordering for PowerUsageSummary. @VisibleForTesting static final int MENU_STATS_REFRESH = Menu.FIRST + 1; private static final String TAG = "PowerUsageBase"; + private static final String KEY_REFRESH_TYPE = "refresh_type"; protected BatteryStatsHelper mStatsHelper; protected UserManager mUm; @@ -57,8 +58,8 @@ public abstract class PowerUsageBase extends DashboardFragment setHasOptionsMenu(true); mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(getContext()); - mBatteryBroadcastReceiver.setBatteryChangedListener(() -> { - restartBatteryStatsLoader(); + mBatteryBroadcastReceiver.setBatteryChangedListener(type -> { + restartBatteryStatsLoader(type); }); } @@ -81,11 +82,14 @@ public abstract class PowerUsageBase extends DashboardFragment mBatteryBroadcastReceiver.unRegister(); } - protected void restartBatteryStatsLoader() { - getLoaderManager().restartLoader(0, Bundle.EMPTY, this); + protected void restartBatteryStatsLoader(int refreshType) { + final Bundle bundle = new Bundle(); + bundle.putInt(KEY_REFRESH_TYPE, refreshType); + + getLoaderManager().restartLoader(0, bundle, new PowerLoaderCallback()); } - protected abstract void refreshUi(); + protected abstract void refreshUi(@BatteryUpdateType int refreshType); protected void updatePreference(BatteryHistoryPreference historyPref) { final long startTime = System.currentTimeMillis(); @@ -93,21 +97,30 @@ public abstract class PowerUsageBase extends DashboardFragment BatteryUtils.logRuntime(TAG, "updatePreference", startTime); } - @Override - public Loader onCreateLoader(int id, - Bundle args) { - return new BatteryStatsHelperLoader(getContext()); - } + /** + * {@link android.app.LoaderManager.LoaderCallbacks} for {@link PowerUsageBase} to load + * the {@link BatteryStatsHelper} + */ + public class PowerLoaderCallback implements LoaderManager.LoaderCallbacks { + private int mRefreshType; - @Override - public void onLoadFinished(Loader loader, - BatteryStatsHelper statsHelper) { - mStatsHelper = statsHelper; - refreshUi(); - } + @Override + public Loader onCreateLoader(int id, + Bundle args) { + mRefreshType = args.getInt(KEY_REFRESH_TYPE); + return new BatteryStatsHelperLoader(getContext()); + } - @Override - public void onLoaderReset(Loader loader) { + @Override + public void onLoadFinished(Loader loader, + BatteryStatsHelper statsHelper) { + mStatsHelper = statsHelper; + refreshUi(mRefreshType); + } + @Override + public void onLoaderReset(Loader loader) { + + } } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 0563815dd71..e4f7a726c17 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -16,6 +16,8 @@ package com.android.settings.fuelgauge; +import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType; + import android.app.Activity; import android.app.LoaderManager; import android.app.LoaderManager.LoaderCallbacks; @@ -277,7 +279,7 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList } else { mStatsType = BatteryStats.STATS_SINCE_CHARGED; } - refreshUi(); + refreshUi(BatteryUpdateType.MANUAL); return true; case MENU_ADVANCED_BATTERY: new SubSettingLauncher(getContext()) @@ -291,14 +293,15 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList } } - protected void refreshUi() { + protected void refreshUi(@BatteryUpdateType int refreshType) { final Context context = getContext(); if (context == null) { return; } - // Only skip BatteryTipLoader for the first time when device is rotated - if (mNeedUpdateBatteryTip) { + // Skip BatteryTipLoader if device is rotated or only battery level change + if (mNeedUpdateBatteryTip + && refreshType != BatteryUpdateType.BATTERY_LEVEL) { restartBatteryTipLoader(); } else { mNeedUpdateBatteryTip = true; @@ -397,8 +400,9 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList } @Override - protected void restartBatteryStatsLoader() { - restartBatteryStatsLoader(true /* clearHeader */); + protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) { + super.restartBatteryStatsLoader(refreshType); + mBatteryHeaderPreferenceController.quickUpdateHeaderPreference(); } @Override @@ -407,13 +411,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList mBatteryTipPreferenceController.saveInstanceState(outState); } - void restartBatteryStatsLoader(boolean clearHeader) { - super.restartBatteryStatsLoader(); - if (clearHeader) { - mBatteryHeaderPreferenceController.quickUpdateHeaderPreference(); - } - } - @Override public void onBatteryTipHandled(BatteryTip batteryTip) { restartBatteryTipLoader(); @@ -428,7 +425,7 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList mContext = context; mLoader = loader; mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext); - mBatteryBroadcastReceiver.setBatteryChangedListener(() -> { + mBatteryBroadcastReceiver.setBatteryChangedListener(type -> { BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() { @Override public void onBatteryInfoLoaded(BatteryInfo info) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java index 3fdbe837425..4583dd1b3b4 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java @@ -15,8 +15,11 @@ */ package com.android.settings.fuelgauge; +import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -70,14 +73,14 @@ public class BatteryBroadcastReceiverTest { } @Test - public void testOnReceive_batteryDataChanged_dataUpdated() { + public void testOnReceive_batteryLevelChanged_dataUpdated() { mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent); assertThat(mBatteryBroadcastReceiver.mBatteryLevel) .isEqualTo(Utils.getBatteryPercentage(mChargingIntent)); assertThat(mBatteryBroadcastReceiver.mBatteryStatus) .isEqualTo(Utils.getBatteryStatus(mContext.getResources(), mChargingIntent)); - verify(mBatteryListener).onBatteryChanged(); + verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL); } @Test @@ -85,7 +88,7 @@ public class BatteryBroadcastReceiverTest { mBatteryBroadcastReceiver.onReceive(mContext, new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); - verify(mBatteryListener).onBatteryChanged(); + verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_SAVER); } @Test @@ -100,7 +103,7 @@ public class BatteryBroadcastReceiverTest { assertThat(mBatteryBroadcastReceiver.mBatteryLevel).isEqualTo(batteryLevel); assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(batteryStatus); - verify(mBatteryListener, never()).onBatteryChanged(); + verify(mBatteryListener, never()).onBatteryChanged(anyInt()); } @Test @@ -115,6 +118,6 @@ public class BatteryBroadcastReceiverTest { assertThat(mBatteryBroadcastReceiver.mBatteryStatus) .isEqualTo(Utils.getBatteryStatus(mContext.getResources(), mChargingIntent)); // 2 times because register will force update the battery - verify(mBatteryListener, times(2)).onBatteryChanged(); + verify(mBatteryListener, times(2)).onBatteryChanged(BatteryUpdateType.MANUAL); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java index 4a905b4603a..9b61e5fe131 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -86,7 +87,7 @@ public class PowerUsageAdvancedTest { @Test public void testOptionsMenu_menuAppToggle_metricEventInvoked() { mFragment.mShowAllApps = false; - doNothing().when(mFragment).restartBatteryStatsLoader(); + doNothing().when(mFragment).restartBatteryStatsLoader(anyInt()); mFragment.onOptionsItemSelected(mToggleAppsMenu); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java index d9f572deefa..eb683c072e0 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java @@ -74,7 +74,7 @@ public class PowerUsageBaseTest { } @Override - protected void refreshUi() { + protected void refreshUi(int refreshType) { // Do nothing } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index b48f00e295b..07341a14656 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -149,7 +149,7 @@ public class PowerUsageSummaryTest { mFragment.initFeatureProvider(); mBatteryMeterView = new BatteryMeterView(mRealContext); mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0); - doNothing().when(mFragment).restartBatteryStatsLoader(); + doNothing().when(mFragment).restartBatteryStatsLoader(anyInt()); doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager(); doReturn(MENU_ADVANCED_BATTERY).when(mAdvancedPageMenu).getItemId(); @@ -316,15 +316,6 @@ public class PowerUsageSummaryTest { verify(mSummary1, times(2)).setOnLongClickListener(any(View.OnLongClickListener.class)); } - @Test - public void restartBatteryStatsLoader_notClearHeader_quickUpdateNotInvoked() { - mFragment.mBatteryHeaderPreferenceController = mBatteryHeaderPreferenceController; - - mFragment.restartBatteryStatsLoader(false /* clearHeader */); - - verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference(); - } - @Test public void optionsMenu_advancedPageEnabled() { when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled()) @@ -360,7 +351,18 @@ public class PowerUsageSummaryTest { when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(false); mFragment.updateBatteryTipFlag(new Bundle()); - mFragment.refreshUi(); + mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL); + + verify(mFragment, never()).restartBatteryTipLoader(); + } + + @Test + public void refreshUi_batteryLevelChanged_doNotUpdateBatteryTip() { + mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class); + when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true); + mFragment.updateBatteryTipFlag(new Bundle()); + + mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_LEVEL); verify(mFragment, never()).restartBatteryTipLoader(); } @@ -371,7 +373,7 @@ public class PowerUsageSummaryTest { when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true); mFragment.updateBatteryTipFlag(new Bundle()); - mFragment.refreshUi(); + mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL); verify(mFragment).restartBatteryTipLoader(); } From 231fe615cc5ae8207d997c6957fe71115043b91e Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Mon, 14 May 2018 13:08:58 -0700 Subject: [PATCH 5/9] Add cancel button for private dns dialog Also update the onclick method. Change-Id: I69e92584e056a4d0a686153315b8df002d91e204 Fixes: 79479021 Test: RunSettingsRoboTests --- res/xml/network_and_internet.xml | 3 +- .../PrivateDnsModeDialogPreference.java | 20 ++++++----- .../PrivateDnsModeDialogPreferenceTest.java | 35 +++++++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/res/xml/network_and_internet.xml b/res/xml/network_and_internet.xml index 064d625681b..69d8fcac784 100644 --- a/res/xml/network_and_internet.xml +++ b/res/xml/network_and_internet.xml @@ -97,6 +97,7 @@ android:order="15" android:dialogTitle="@string/select_private_dns_configuration_dialog_title" android:dialogLayout="@layout/private_dns_mode_dialog" - android:positiveButtonText="@string/save" /> + android:positiveButtonText="@string/save" + android:negativeButtonText="@android:string/cancel" /> diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java index 178aa27c188..969cb11920e 100644 --- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java +++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java @@ -156,16 +156,18 @@ public class PrivateDnsModeDialogPreference extends CustomDialogPreference imple @Override public void onClick(DialogInterface dialog, int which) { - final Context context = getContext(); - if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { - // Only clickable if hostname is valid, so we could save it safely - Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY, - mEditText.getText().toString()); - } + if (which == DialogInterface.BUTTON_POSITIVE) { + final Context context = getContext(); + if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { + // Only clickable if hostname is valid, so we could save it safely + Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY, + mEditText.getText().toString()); + } - FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, - MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); - Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode); + FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, + MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); + Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode); + } } @Override diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java index e9ffa8ac4c6..dfea6fba515 100644 --- a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java @@ -28,7 +28,10 @@ import static org.mockito.Mockito.when; import android.app.AlertDialog; import android.app.Fragment; +import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; +import android.net.ConnectivityManager; import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; @@ -150,4 +153,36 @@ public class PrivateDnsModeDialogPreferenceTest { assertThat(mSaveButton.isEnabled()).named("provider: " + invalid).isFalse(); } } + + @Test + public void testOnClick_positiveButtonClicked_saveData() { + // Set the default settings to OFF + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putString(contentResolver, Settings.Global.PRIVATE_DNS_MODE, + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + + mPreference.mMode = ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; + mPreference.onClick(null, DialogInterface.BUTTON_POSITIVE); + + // Change to OPPORTUNISTIC + assertThat(Settings.Global.getString(contentResolver, + Settings.Global.PRIVATE_DNS_MODE)).isEqualTo( + ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC); + } + + @Test + public void testOnClick_negativeButtonClicked_doNothing() { + // Set the default settings to OFF + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putString(contentResolver, Settings.Global.PRIVATE_DNS_MODE, + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + + mPreference.mMode = ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; + mPreference.onClick(null, DialogInterface.BUTTON_NEGATIVE); + + // Still equal to OFF + assertThat(Settings.Global.getString(contentResolver, + Settings.Global.PRIVATE_DNS_MODE)).isEqualTo( + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + } } From e6bf71abedb8da622b634fd8e19cf3ac9cd999b2 Mon Sep 17 00:00:00 2001 From: Naina Nalluri Date: Wed, 2 May 2018 14:30:11 -0700 Subject: [PATCH 6/9] DO NOT MERGE Add Slices for WifiCalling Add Slices for WifiCalling WifiCalling Slice Provider: 1. If there is no activation needed or if the Wifi calling is currently turned on - provide the slice to toggle the value 2. Else display appropriate message with further instructions WifiCalling Slice Broadcast Receiver: 1. If the action is turning off wifi or if there is no activation needed. Change the setting with ImsManager. 2. And Ask to requery the slice in one second to display updated settings if 1 is valid or display appropriate message Bug: 63731862 Bug: 78192106 Test: Use support-slices-demos-debug.apk to test on device Test: Robotests Change-Id: I29e1822fd24ebcff575fa48ad93f84ed91bf4d87 --- res/values/strings.xml | 11 + .../slices/SettingsSliceProvider.java | 18 +- .../slices/SliceBroadcastReceiver.java | 7 + .../slices/SlicesFeatureProvider.java | 9 +- .../slices/SlicesFeatureProviderImpl.java | 8 +- .../wifi/calling/WifiCallingSliceHelper.java | 363 ++++++++++++++++++ .../calling/WifiCallingSliceHelperTest.java | 316 +++++++++++++++ 7 files changed, 725 insertions(+), 7 deletions(-) create mode 100644 src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java create mode 100644 tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index b061e611ab2..3a185c0727b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2348,6 +2348,17 @@ Learn more about Private DNS features + + Setting managed by carrier + + Activate Wi\u2011Fi Calling + + Turn on Wi\u2011Fi calling + + Wi\u2011Fi calling is not supported for %1$s + + Carrier Display diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index df826cb8cef..9889c765356 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -18,6 +18,8 @@ package com.android.settings.slices; import static android.Manifest.permission.READ_SEARCH_INDEXABLES; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.PATH_WIFI_CALLING; + import android.app.PendingIntent; import android.app.slice.SliceManager; import android.content.ContentResolver; @@ -36,7 +38,13 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Pair; +import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.ThreadUtils; import java.net.URISyntaxException; @@ -46,11 +54,6 @@ import java.util.List; import java.util.Map; import java.util.WeakHashMap; -import androidx.slice.Slice; -import androidx.slice.SliceProvider; -import androidx.slice.builders.ListBuilder; -import androidx.slice.builders.SliceAction; - /** * A {@link SliceProvider} for Settings to enabled inline results in system apps. * @@ -165,6 +168,11 @@ public class SettingsSliceProvider extends SliceProvider { switch (path) { case "/" + PATH_WIFI: return createWifiSlice(sliceUri); + case "/" + PATH_WIFI_CALLING: + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingSlice(sliceUri); } SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 47a7f5a45bd..04097340ed3 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -21,6 +21,7 @@ import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CH import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED; import android.app.slice.Slice; import android.content.BroadcastReceiver; @@ -79,6 +80,12 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { context.getContentResolver().notifyChange(uri, null); }, 1000); break; + case ACTION_WIFI_CALLING_CHANGED: + FeatureFactory.getFactory(context) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(context) + .handleWifiCallingChanged(intent); + break; } } diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java index e5bba617e48..8dd6547b398 100644 --- a/src/com/android/settings/slices/SlicesFeatureProvider.java +++ b/src/com/android/settings/slices/SlicesFeatureProvider.java @@ -2,6 +2,8 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; + /** * Manages Slices in Settings. */ @@ -24,4 +26,9 @@ public interface SlicesFeatureProvider { * If the data is already indexed, the data will not change. */ void indexSliceData(Context context); -} \ No newline at end of file + + /** + * Gets new WifiCallingSliceHelper object + */ + WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context); +} diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java index 8e5bc067150..16684bfb022 100644 --- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java +++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java @@ -2,6 +2,7 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settingslib.utils.ThreadUtils; /** @@ -39,4 +40,9 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider { SlicesIndexer indexer = getSliceIndexer(context); indexer.indexSliceData(); } -} \ No newline at end of file + + @Override + public WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context) { + return new WifiCallingSliceHelper(context); + } +} diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java new file mode 100644 index 00000000000..7213148e97f --- /dev/null +++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2018 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.wifi.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.PersistableBundle; +import android.support.v4.graphics.drawable.IconCompat; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + +import com.android.ims.ImsManager; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +/** + * Helper class to control slices for wifi calling settings. + */ +public class WifiCallingSliceHelper { + + private static final String TAG = "WifiCallingSliceHelper"; + + /** + * Settings slice path to wifi calling setting. + */ + public static final String PATH_WIFI_CALLING = "wifi_calling"; + + /** + * Action passed for changes to wifi calling slice (toggle). + */ + public static final String ACTION_WIFI_CALLING_CHANGED = + "com.android.settings.wifi.calling.action.WIFI_CALLING_CHANGED"; + + /** + * Action for Wifi calling Settings activity which + * allows setting configuration for Wifi calling + * related settings + */ + public static final String ACTION_WIFI_CALLING_SETTINGS_ACTIVITY = + "android.settings.WIFI_CALLING_SETTINGS"; + + /** + * Timeout for querying wifi calling setting from ims manager. + */ + private static final int TIMEOUT_MILLIS = 2000; + + /** + * Time for which data contained in the slice can remain fresh. + */ + private static final int SLICE_TTL_MILLIS = 60000; + + protected SubscriptionManager mSubscriptionManager; + private final Context mContext; + + @VisibleForTesting + public WifiCallingSliceHelper(Context context) { + mContext = context; + } + + /** + * Returns Slice object for wifi calling settings. + * + * If wifi calling is being turned on and if wifi calling activation is needed for the current + * carrier, this method will return Slice with instructions to go to Settings App. + * + * If wifi calling is not supported for the current carrier, this method will return slice with + * not supported message. + * + * If wifi calling setting can be changed, this method will return the slice to toggle wifi + * calling option with ACTION_WIFI_CALLING_CHANGED as endItem. + */ + public Slice createWifiCallingSlice(Uri sliceUri) { + final int subId = getDefaultVoiceSubId(); + final String carrierName = getSimCarrierName(); + + if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.d(TAG, "Invalid subscription Id"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + final ImsManager imsManager = getImsManager(subId); + + if (!imsManager.isWfcEnabledByPlatform() + || !imsManager.isWfcProvisionedOnDevice()) { + Log.d(TAG, "Wifi calling is either not provisioned or not enabled by Platform"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + try { + final boolean isWifiCallingEnabled = isWifiCallingEnabled(imsManager); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + + // Send this actionable wifi calling slice to toggle the setting + // only when there is no need for wifi calling activation with the server + if (activationAppIntent != null && !isWifiCallingEnabled) { + Log.d(TAG, "Needs Activation"); + // Activation needed for the next action of the user + // Give instructions to go to settings app + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString( + R.string.wifi_calling_settings_activation_instructions), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + return getWifiCallingSlice(sliceUri, mContext, isWifiCallingEnabled); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + Log.e(TAG, "Unable to read the current WiFi calling status", e); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_turn_on), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + } + + private boolean isWifiCallingEnabled(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + final FutureTask isWifiOnTask = new FutureTask<>(new Callable() { + @Override + public Boolean call() { + return imsManager.isWfcEnabledByUser(); + } + }); + final ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(isWifiOnTask); + + Boolean isWifiEnabledByUser = false; + isWifiEnabledByUser = isWifiOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + + return isWifiEnabledByUser && imsManager.isNonTtyOrTtyOnVolteEnabled(); + } + + /** + * Builds a toggle slice where the intent takes you to the wifi calling page and the toggle + * enables/disables wifi calling. + */ + private Slice getWifiCallingSlice(Uri sliceUri, Context mContext, + boolean isWifiCallingEnabled) { + + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + final String title = mContext.getString(R.string.wifi_calling_settings_title); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .addEndItem( + new SliceAction( + getBroadcastIntent(ACTION_WIFI_CALLING_CHANGED), + null /* actionTitle */, isWifiCallingEnabled)) + .setPrimaryAction(new SliceAction( + getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY), + icon, + title))) + .build(); + } + + protected ImsManager getImsManager(int subId) { + return ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(subId)); + } + + private Integer getWfcMode(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + FutureTask wfcModeTask = new FutureTask<>(new Callable() { + @Override + public Integer call() { + return imsManager.getWfcMode(false); + } + }); + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(wfcModeTask); + return wfcModeTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } + + /** + * Handles wifi calling setting change from wifi calling slice and posts notification. Should be + * called when intent action is ACTION_WIFI_CALLING_CHANGED. Executed in @WorkerThread + * + * @param intent action performed + */ + public void handleWifiCallingChanged(Intent intent) { + final int subId = getDefaultVoiceSubId(); + + if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + final ImsManager imsManager = getImsManager(subId); + if (imsManager.isWfcEnabledByPlatform() + || imsManager.isWfcProvisionedOnDevice()) { + final boolean currentValue = imsManager.isWfcEnabledByUser() + && imsManager.isNonTtyOrTtyOnVolteEnabled(); + final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, + currentValue); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + if (!newValue || activationAppIntent == null) { + // If either the action is to turn off wifi calling setting + // or there is no activation involved - Update the setting + if (newValue != currentValue) { + imsManager.setWfcSetting(newValue); + } + } + } + } + // notify change in slice in any case to get re-queried. This would result in displaying + // appropriate message with the updated setting. + final Uri uri = SliceBuilderUtils.getUri(PATH_WIFI_CALLING, false /*isPlatformSlice*/); + mContext.getContentResolver().notifyChange(uri, null); + } + + /** + * Returns Slice with the title and subtitle provided as arguments with wifi signal Icon. + * + * @param title Title of the slice + * @param subtitle Subtitle of the slice + * @param sliceUri slice uri + * @return Slice with title and subtitle + */ + // TODO(b/79548264) asses different scenarios and return null instead of non-actionable slice + private Slice getNonActionableWifiCallingSlice(String title, String subtitle, Uri sliceUri, + PendingIntent primaryActionIntent) { + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .setSubtitle(subtitle) + .setPrimaryAction(new SliceAction( + primaryActionIntent, icon, + title))) + .build(); + } + + /** + * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise. + */ + private boolean isCarrierConfigManagerKeyEnabled(Context mContext, String key, + int subId, boolean defaultValue) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + boolean ret = false; + if (configManager != null) { + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle != null) { + ret = bundle.getBoolean(key, defaultValue); + } + } + return ret; + } + + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mContext.getSystemService(CarrierConfigManager.class); + } + + /** + * Returns the current default voice subId obtained from SubscriptionManager + */ + protected int getDefaultVoiceSubId() { + if (mSubscriptionManager == null) { + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + } + return SubscriptionManager.getDefaultVoiceSubscriptionId(); + } + + /** + * Returns Intent of the activation app required to activate wifi calling or null if there is no + * need for activation. + */ + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + if (configManager == null) { + return null; + } + + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle == null) { + return null; + } + + final String carrierApp = bundle.getString( + CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); + if (TextUtils.isEmpty(carrierApp)) { + return null; + } + + final ComponentName componentName = ComponentName.unflattenFromString(carrierApp); + if (componentName == null) { + return null; + } + + final Intent intent = new Intent(); + intent.setComponent(componentName); + return intent; + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + /** + * Returns PendingIntent to start activity specified by action + */ + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + /** + * Returns carrier id name of the current Subscription + */ + private String getSimCarrierName() { + final TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); + final CharSequence carrierName = telephonyManager.getSimCarrierIdName(); + if (carrierName == null) { + return mContext.getString(R.string.carrier); + } + return carrierName.toString(); + } + +} diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java new file mode 100644 index 00000000000..ac3ff3ff635 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2018 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.wifi.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static android.app.slice.Slice.HINT_TITLE; +import static android.app.slice.SliceItem.FORMAT_TEXT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.telephony.CarrierConfigManager; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceMetadata; +import androidx.slice.SliceProvider; +import androidx.slice.core.SliceAction; +import androidx.slice.core.SliceQuery; +import androidx.slice.widget.SliceLiveData; + +import com.android.ims.ImsManager; +import com.android.settings.R; +import com.android.settings.slices.SettingsSliceProvider; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; +import com.android.settings.slices.SliceData; +import com.android.settings.slices.SlicesFeatureProvider; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiCallingSliceHelperTest { + + private Context mContext; + @Mock + private CarrierConfigManager mMockCarrierConfigManager; + + @Mock + private ImsManager mMockImsManager; + + private final Uri mWfcURI = Uri.parse("content://com.android.settings.slices/wifi_calling"); + + private FakeWifiCallingSliceHelper mWfcSliceHelper; + private SettingsSliceProvider mProvider; + private SliceBroadcastReceiver mReceiver; + private FakeFeatureFactory mFeatureFactory; + private SlicesFeatureProvider mSlicesFeatureProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + + //setup for SettingsSliceProvider tests + mProvider = spy(new SettingsSliceProvider()); + doReturn(mContext).when(mProvider).getContext(); + + //setup for SliceBroadcastReceiver test + mReceiver = spy(new SliceBroadcastReceiver()); + + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); + + // Prevent crash in SliceMetadata. + Resources resources = spy(mContext.getResources()); + doReturn(60).when(resources).getDimensionPixelSize(anyInt()); + doReturn(resources).when(mContext).getResources(); + + mWfcSliceHelper = new FakeWifiCallingSliceHelper(mContext); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @Test + public void test_CreateWifiCallingSlice_invalidSubId() { + mWfcSliceHelper.setDefaultVoiceSubId(-1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_wfcNotSupported() { + doReturn(false).when(mMockImsManager).isWfcEnabledByPlatform(); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_needsActivation() { + /* In cases where activation is needed and the user action + would be turning on the wifi calling (i.e. if wifi calling is + turned off) we need to guide the user to wifi calling settings + activity so the user can perform the activation there.(PrimaryAction) + */ + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(false).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + + @Test + public void test_CreateWifiCallingSlice_success() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SettingSliceProvider_getsRightSliceWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + + final Slice slice = mProvider.onBindSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SliceBroadcastReceiver_toggleOffWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + mWfcSliceHelper.setActivationAppIntent(null); + + ArgumentCaptor mWfcSettingCaptor = ArgumentCaptor.forClass(Boolean.class); + + // turn on Wifi calling setting + Intent intent = new Intent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + intent.putExtra(EXTRA_TOGGLE_STATE, true); + + // change the setting + mReceiver.onReceive(mContext, intent); + + verify((mMockImsManager)).setWfcSetting(mWfcSettingCaptor.capture()); + + // assert the change + assertThat(mWfcSettingCaptor.getValue()).isTrue(); + } + + private void testWifiCallingSettingsUnavailableSlice(Slice slice, + SliceData sliceData, PendingIntent expectedPrimaryAction) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + //Check there is no toggle action + final List toggles = metadata.getToggles(); + assertThat(toggles).isEmpty(); + + // Check whether the primary action is to open wifi calling settings activity + final PendingIntent primaryPendingIntent = + metadata.getPrimaryAction().getAction(); + assertThat(primaryPendingIntent).isEqualTo(expectedPrimaryAction); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private void testWifiCallingSettingsToggleSlice(Slice slice, + SliceData sliceData) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + + final SliceAction mainToggleAction = toggles.get(0); + + // Check intent in Toggle Action + final PendingIntent togglePendingIntent = mainToggleAction.getAction(); + final PendingIntent expectedToggleIntent = getBroadcastIntent( + WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + assertThat(togglePendingIntent).isEqualTo(expectedToggleIntent); + + // Check primary intent + final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction(); + final PendingIntent expectedPendingIntent = + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY); + assertThat(primaryPendingIntent).isEqualTo(expectedPendingIntent); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + private void assertTitle(List sliceItems, String title) { + boolean hasTitle = false; + for (SliceItem item : sliceItems) { + List titleItems = SliceQuery.findAll(item, FORMAT_TEXT, HINT_TITLE, + null /* non-hints */); + if (titleItems == null) { + continue; + } + + hasTitle = true; + for (SliceItem subTitleItem : titleItems) { + assertThat(subTitleItem.getText()).isEqualTo(title); + } + } + assertThat(hasTitle).isTrue(); + } + private class FakeWifiCallingSliceHelper extends WifiCallingSliceHelper { + int mSubId = 1; + + private Intent mActivationAppIntent; + FakeWifiCallingSliceHelper(Context context) { + super(context); + mActivationAppIntent = null; + } + + @Override + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mMockCarrierConfigManager; + } + + @Override + protected ImsManager getImsManager(int subId) { + return mMockImsManager; + } + + protected int getDefaultVoiceSubId() { + return mSubId; + } + + protected void setDefaultVoiceSubId(int id) { + mSubId = id; + } + + @Override + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + return mActivationAppIntent; + } + + public void setActivationAppIntent(Intent intent) { + mActivationAppIntent = intent; + } + } +} From 80f6aab9b18cb54b955265e83c29bb6b965a8552 Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Mon, 14 May 2018 13:42:12 -0700 Subject: [PATCH 7/9] Remove the dynamic summary for bt preference. Starting from b/62069132, we should use static summary for it. Change-Id: I8ba5a9d6e8b5bcbcd9f4369cdf44004d557257a3 Fixes: 79666649 Test: Manual --- src/com/android/settings/TetherSettings.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 94c3fb581fa..1e56e540dcc 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -387,7 +387,6 @@ public class TetherSettings extends RestrictedSettingsFragment if (adapter.getState() == BluetoothAdapter.STATE_OFF) { mBluetoothEnableForTether = true; adapter.enable(); - mBluetoothTether.setSummary(R.string.bluetooth_turning_on); mBluetoothTether.setEnabled(false); return; } From dae3c912d770cfcafc7d357d72a72eb2ea16eced Mon Sep 17 00:00:00 2001 From: Lei Yu Date: Mon, 14 May 2018 17:55:21 -0700 Subject: [PATCH 8/9] Add string for disabled FAS toggle. If FAS toggle is disabled because app is in doze whitelist(not optimized), show a dialog that has this string. Bug: 77924141 Test: Manual Change-Id: I30518cd887125bff00b7c567e26204d5b1814858 --- res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 5c33e9c0700..c190282888f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4915,6 +4915,8 @@ Limit background activity? If you limit background activity for an app, it may misbehave + + Since this app is not set to optimize battery, you can\u2019t restrict it.\n\nTo restrict the app, first turn on battery optimization. Screen usage since full charge From bafae44cb26c5751006d639e906537c5d3ffca1d Mon Sep 17 00:00:00 2001 From: timhypeng Date: Tue, 24 Apr 2018 21:45:23 +0800 Subject: [PATCH 9/9] Click left side of list in "previously connected devices" should connect to the device * In "previously connected devices", to connect device * In "Available media devices", to activate device Bug: 77607104 Test: make -j50 RunSettingsRoboTests Change-Id: If5005031bd3f5dc4950abf2c77575785cd6f67b1 Merged-In: If5005031bd3f5dc4950abf2c77575785cd6f67b1 --- .../AvailableMediaBluetoothDeviceUpdater.java | 11 +++++++++- .../bluetooth/BluetoothDeviceUpdater.java | 20 +++++-------------- .../SavedBluetoothDeviceUpdater.java | 14 ++++++++++++- ...ilableMediaBluetoothDeviceUpdaterTest.java | 9 +++++++++ .../SavedBluetoothDeviceUpdaterTest.java | 11 +++++++++- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java index c8e6f26038f..01c1ff61832 100644 --- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java @@ -25,11 +25,13 @@ import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import android.support.v7.preference.Preference; /** * Controller to maintain available media Bluetooth devices */ -public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater { +public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { private static final String TAG = "AvailableMediaBluetoothDeviceUpdater"; private static final boolean DBG = false; @@ -116,5 +118,12 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater } return isFilterMatched; } + + @Override + public boolean onPreferenceClick(Preference preference) { + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + return device.setActive(); + } } diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java index 8937b177a64..bab71713a2a 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java @@ -59,24 +59,12 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, protected DashboardFragment mFragment; private final boolean mShowDeviceWithoutNames; - private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null; - + @VisibleForTesting final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> { launchDeviceDetails(pref); }; - private class PreferenceClickListener implements - Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - final CachedBluetoothDevice device = - ((BluetoothDevicePreference) preference).getBluetoothDevice(); - Log.i(TAG, "OnPreferenceClickListener: device=" + device); - return device.setActive(); - } - } - public BluetoothDeviceUpdater(Context context, DashboardFragment fragment, DevicePreferenceCallback devicePreferenceCallback) { this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(context)); @@ -91,7 +79,6 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false); mPreferenceMap = new HashMap<>(); mLocalManager = localManager; - mDevicePreferenceClickListener = new PreferenceClickListener(); } /** @@ -212,7 +199,10 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, new BluetoothDevicePreference(mPrefContext, cachedDevice, mShowDeviceWithoutNames); btPreference.setOnGearClickListener(mDeviceProfilesListener); - btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener); + if (this instanceof Preference.OnPreferenceClickListener) { + btPreference.setOnPreferenceClickListener( + (Preference.OnPreferenceClickListener)this); + } mPreferenceMap.put(device, btPreference); mDevicePreferenceCallback.onDeviceAdded(btPreference); } diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java index 34cb5746c60..f087c6ac7d3 100644 --- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java @@ -24,11 +24,15 @@ import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import android.support.v7.preference.Preference; +import android.util.Log; /** * Maintain and update saved bluetooth devices(bonded but not connected) */ -public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { +public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { + private static final String TAG = "SavedBluetoothDeviceUpdater"; public SavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment, DevicePreferenceCallback devicePreferenceCallback) { @@ -57,4 +61,12 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { final BluetoothDevice device = cachedDevice.getDevice(); return device.getBondState() == BluetoothDevice.BOND_BONDED && !device.isConnected(); } + + @Override + public boolean onPreferenceClick(Preference preference) { + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + device.connect(true); + return true; + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java index ed9501554eb..ced8fc4a2e4 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java @@ -73,6 +73,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { private AvailableMediaBluetoothDeviceUpdater mBluetoothDeviceUpdater; private Collection cachedDevices; private ShadowAudioManager mShadowAudioManager; + private BluetoothDevicePreference mPreference; @Before public void setUp() { @@ -93,6 +94,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { mBluetoothDeviceUpdater = spy(new AvailableMediaBluetoothDeviceUpdater(mDashboardFragment, mDevicePreferenceCallback, mLocalManager)); mBluetoothDeviceUpdater.setPrefContext(mContext); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false); doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); doNothing().when(mBluetoothDeviceUpdater).removePreference(any()); } @@ -208,5 +210,12 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); } + + @Test + public void onClick_Preference_setActive() { + mBluetoothDeviceUpdater.onPreferenceClick(mPreference); + + verify(mCachedBluetoothDevice).setActive(); + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java index fae014f7b10..547727beced 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java @@ -57,7 +57,8 @@ public class SavedBluetoothDeviceUpdaterTest { private LocalBluetoothProfileManager mLocalBluetoothProfileManager; private Context mContext; - private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private SavedBluetoothDeviceUpdater mBluetoothDeviceUpdater; + private BluetoothDevicePreference mPreference; @Before public void setUp() { @@ -71,6 +72,7 @@ public class SavedBluetoothDeviceUpdaterTest { mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mDashboardFragment, mDevicePreferenceCallback, mLocalManager)); mBluetoothDeviceUpdater.setPrefContext(mContext); + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false); doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); doNothing().when(mBluetoothDeviceUpdater).removePreference(any()); } @@ -110,4 +112,11 @@ public class SavedBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); } + + @Test + public void onClick_Preference_setConnect() { + mBluetoothDeviceUpdater.onPreferenceClick(mPreference); + + verify(mCachedBluetoothDevice).connect(true); + } }