Merge "Update Accessbility vibration settings." into pi-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e8e45e3..060429e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1114,7 +1114,9 @@
</intent-filter>
</activity>
- <activity android:name=".slice.SliceDeepLinkSpringBoard"
+ <activity
+ android:name=".slices.SliceDeepLinkSpringBoard"
+ android:excludeFromRecents="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
diff --git a/res/values/config.xml b/res/values/config.xml
index e3ec74f..f8a7acc 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -131,4 +131,7 @@
<!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
<string-array name="config_settings_slices_accessibility_components" translatable="false"/>
+ <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
+ <bool name="config_swipe_up_gesture_setting_available">false</bool>
+
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 104b3c8..8bdfb5f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3386,6 +3386,8 @@
<!-- Master Clear -->
<!-- Button title to factory data reset the entire device -->
<string name="master_clear_title">Erase all data (factory reset)</string>
+ <!-- Button title to factory data reset the entire device. The "(factory reset)" part is optional for translation. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=3531267871084279512]-->
+ <string name="master_clear_short_title">Erase all data (factory reset)</string>
<!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
<string name="master_clear_desc" product="tablet">"This will erase all data from your tablet\u2019s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and app data and settings</li>\n<li>Downloaded apps</li>"</string>
<!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
@@ -4927,9 +4929,7 @@
<string name="device_cellular_network">Mobile network scanning</string>
<!-- Label for list of apps using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
- <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="relative_time">^1</xliff:g>)</string>
- <!-- Label for device components using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
- <string name="power_usage_list_summary_device">Device usage since full charge (<xliff:g id="relative_time">^1</xliff:g>)</string>
+ <string name="power_usage_list_summary">Battery usage since full charge</string>
<!-- Temp string used to debug new battery estimates [DO NOT TRANSLATE] -->
<string name="power_usage_enhanced_debug" translatable="false"><xliff:g id="time">%1$s</xliff:g> left (New ML est)</string>
<!-- Temp string used to debug old battery estimates [DO NOT TRANSLATE] -->
@@ -5034,11 +5034,11 @@
<!-- Summary for the battery high usage tip, which presents battery may run out soon [CHAR LIMIT=NONE] -->
<string name="battery_tip_high_usage_summary">Battery may run out soon</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+ <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+ <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+ <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
<!-- Title for restricted app preference, showing how many app need to be restricted [CHAR LIMIT=NONE] -->
<plurals name="battery_tip_restrict_title">
<item quantity="one">Restrict %1$d app</item>
@@ -7931,6 +7931,15 @@
<!-- [CHAR LIMIT=20] Zen mode settings: Calls screen footer -->
<string name="zen_mode_calls_footer">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.</string>
+ <!-- [CHAR LIMIT=50] Zen mode settings: Starred contacts preference title -->
+ <string name="zen_mode_starred_contacts_title">Starred contacts</string>
+
+ <!-- Zen mode settings: Starred contacts summary [CHAR LIMIT=NONE] -->
+ <plurals name="zen_mode_starred_contacts_summary_additional_contacts">
+ <item quantity="one">and 1 other</item>
+ <item quantity="other">and <xliff:g id="num_people" example="3">%d</xliff:g> others</item>
+ </plurals>
+
<!-- [CHAR LIMIT=20] Zen mode settings: Messages option -->
<string name="zen_mode_messages">Messages</string>
@@ -9862,6 +9871,9 @@
<!-- Help URI, battery saver page [DO NOT TRANSLATE] -->
<string name="help_url_battery_saver_settings" translatable="false"></string>
+ <!-- Help URI, smart battery page [DO NOT TRANSLATE] -->
+ <string name="help_uri_smart_battery_settings" translatable="false"></string>
+
<!-- Title label for new device suggestion, which is displayed in Settings homepage [CHAR LIMIT=100] -->
<string name="new_device_suggestion_title">What\'s new and exciting?</string>
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index ec25520..407feb6 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -15,5 +15,19 @@
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="bluetooth_switchbar_screen"
- android:title="@string/bluetooth_settings_title" />
\ No newline at end of file
+ android:title="@string/bluetooth_settings_title">
+
+ <com.android.settingslib.RestrictedPreference
+ android:key="bluetooth_screen_add_bt_devices"
+ android:title="@string/connected_device_add_device_title"
+ android:icon="@drawable/ic_menu_add"
+ android:summary="@string/connected_device_add_device_summary"
+ android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
+ settings:allowDividerAbove="true"
+ settings:userRestriction="no_config_bluetooth"
+ settings:useAdminDisabledSummary="true"
+ settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml
index d9e47f3..62d9ef4 100644
--- a/res/xml/zen_mode_calls_settings.xml
+++ b/res/xml/zen_mode_calls_settings.xml
@@ -29,7 +29,9 @@
android:entries="@array/zen_mode_contacts_entries"
android:entryValues="@array/zen_mode_contacts_values"/>
- <!-- TODO: setting that links to contacts or placeholder for external app -->
+ <Preference
+ android:key="zen_mode_starred_contacts"
+ android:title="@string/zen_mode_starred_contacts_title"/>
<!-- Repeat callers -->
<SwitchPreference
diff --git a/res/xml/zen_mode_msg_event_reminder_settings.xml b/res/xml/zen_mode_msg_event_reminder_settings.xml
index 866f333..2f065a6 100644
--- a/res/xml/zen_mode_msg_event_reminder_settings.xml
+++ b/res/xml/zen_mode_msg_event_reminder_settings.xml
@@ -29,6 +29,10 @@
android:entries="@array/zen_mode_contacts_entries"
android:entryValues="@array/zen_mode_contacts_values"/>
+ <Preference
+ android:key="zen_mode_starred_contacts"
+ android:title="@string/zen_mode_starred_contacts_title"/>
+
<!-- Reminders -->
<SwitchPreference
android:key="zen_mode_reminders"
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 766c6ae..8f573c0 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -102,7 +102,7 @@
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getActivity().setTitle(R.string.master_clear_title);
+ getActivity().setTitle(R.string.master_clear_short_title);
}
/**
@@ -114,7 +114,7 @@
private boolean runKeyguardConfirmation(int request) {
Resources res = getActivity().getResources();
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
- request, res.getText(R.string.master_clear_title));
+ request, res.getText(R.string.master_clear_short_title));
}
@VisibleForTesting
diff --git a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
index 44d5b79..aa6d4f3 100644
--- a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
@@ -32,6 +32,8 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
+import com.android.settings.search.actionbar.SearchMenuController;
+import com.android.settings.support.actionbar.HelpResourceProvider;
import java.util.Arrays;
import java.util.List;
@@ -88,6 +90,10 @@
// If invoked from SUW, redirect to fragment instrumented for Vision Settings metrics
preference.setFragment(
ToggleScreenMagnificationPreferenceFragmentForSetupWizard.class.getName());
+ Bundle args = preference.getExtras();
+ // Copy from AccessibilitySettingsForSetupWizardActivity, hide search and help menu
+ args.putInt(HelpResourceProvider.HELP_URI_RESOURCE_KEY, 0);
+ args.putBoolean(SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR, false);
}
return super.onPreferenceTreeClick(preference);
}
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index 7b7fb5b..5e45c43 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -27,7 +27,9 @@
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.development.DevelopmentSettingsDashboardFragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.display.NightDisplaySettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.gestures.GestureSettings;
import com.android.settings.language.LanguageAndInputSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.notification.ConfigureNotificationSettings;
@@ -95,6 +97,10 @@
CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
CategoryKey.CATEGORY_DO_NOT_DISTURB);
+ PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
+ CategoryKey.CATEGORY_GESTURES);
+ PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
+ CategoryKey.CATEGORY_NIGHT_LIGHT);
CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index b5b98c35..01770a2 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -193,14 +193,7 @@
}
mBatteryStatsHelper = statsHelper;
- final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(
- mBatteryStatsHelper, System.currentTimeMillis());
- final CharSequence timeSequence = StringUtil.formatRelativeTime(mContext,
- lastFullChargeTime,
- false);
- final int resId = showAllApps ? R.string.power_usage_list_summary_device
- : R.string.power_usage_list_summary;
- mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
+ mAppListGroup.setTitle(R.string.power_usage_list_summary);
final PowerProfile powerProfile = statsHelper.getPowerProfile();
final BatteryStats stats = statsHelper.getStats();
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index f78d236..a1f5df2 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -521,7 +521,7 @@
return true;
}
- return isSystemUid(uid) || powerWhitelistBackend.isSysWhitelistedExceptIdle(packageNames)
+ return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames)
|| (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames));
}
diff --git a/src/com/android/settings/fuelgauge/SmartBatterySettings.java b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
index 5e63976..d954e83 100644
--- a/src/com/android/settings/fuelgauge/SmartBatterySettings.java
+++ b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
@@ -60,6 +60,11 @@
}
@Override
+ public int getHelpResource() {
+ return R.string.help_uri_smart_battery_settings;
+ }
+
+ @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, (SettingsActivity) getActivity(), this);
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
index 2fac9b6..b6bcd54 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
@@ -71,7 +71,8 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
- private boolean mIsJobCanceled = false;
+ @VisibleForTesting
+ boolean mIsJobCanceled = false;
public static void scheduleAnomalyDetection(Context context, Intent intent) {
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
@@ -89,6 +90,9 @@
@Override
public boolean onStartJob(JobParameters params) {
+ synchronized (mLock) {
+ mIsJobCanceled = false;
+ }
ThreadUtils.postOnBackgroundThread(() -> {
final Context context = AnomalyDetectionJobService.this;
final BatteryDatabaseManager batteryDatabaseManager =
@@ -96,18 +100,15 @@
final BatteryTipPolicy policy = new BatteryTipPolicy(this);
final BatteryUtils batteryUtils = BatteryUtils.getInstance(this);
final ContentResolver contentResolver = getContentResolver();
- final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(this,
- true /* collectBatteryBroadcast */);
final UserManager userManager = getSystemService(UserManager.class);
final PowerWhitelistBackend powerWhitelistBackend = PowerWhitelistBackend.getInstance();
final PowerUsageFeatureProvider powerUsageFeatureProvider = FeatureFactory
.getFactory(this).getPowerUsageFeatureProvider(this);
final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory
.getFactory(this).getMetricsFeatureProvider();
- batteryUtils.initBatteryStatsHelper(batteryStatsHelper, null /* bundle */, userManager);
for (JobWorkItem item = dequeueWork(params); item != null; item = dequeueWork(params)) {
- saveAnomalyToDatabase(context, batteryStatsHelper, userManager,
+ saveAnomalyToDatabase(context, userManager,
batteryDatabaseManager, batteryUtils, policy, powerWhitelistBackend,
contentResolver, powerUsageFeatureProvider, metricsFeatureProvider,
item.getIntent().getExtras());
@@ -128,8 +129,7 @@
}
@VisibleForTesting
- void saveAnomalyToDatabase(Context context, BatteryStatsHelper batteryStatsHelper,
- UserManager userManager,
+ void saveAnomalyToDatabase(Context context, UserManager userManager,
BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils,
BatteryTipPolicy policy, PowerWhitelistBackend powerWhitelistBackend,
ContentResolver contentResolver, PowerUsageFeatureProvider powerUsageFeatureProvider,
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index ac6d622..3870e72 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -94,10 +94,7 @@
return new AlertDialog.Builder(context)
.setMessage(getString(R.string.battery_tip_dialog_message,
- highUsageTip.getHighUsageAppList().size(),
- StringUtil.formatRelativeTime(context,
- highUsageTip.getLastFullChargeTimeMs(),
- false /* withSeconds */)))
+ highUsageTip.getHighUsageAppList().size()))
.setView(view)
.setPositiveButton(android.R.string.ok, null)
.create();
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index b9194b4..fc2bbdf 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -67,7 +67,7 @@
final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG);
final Context context = getContext();
- tips.add(new LowBatteryDetector(policy, batteryInfo).detect());
+ tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect());
tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper,
batteryInfo.discharging).detect());
tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
@@ -87,7 +87,8 @@
final List<BatteryTip> tips = new ArrayList<>();
tips.add(new SummaryTip(BatteryTip.StateType.NEW,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN));
- tips.add(new LowBatteryTip(BatteryTip.StateType.NEW));
+ tips.add(new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+ "Fake data"));
return tips;
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 5520bf3..7743372 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -50,6 +50,7 @@
private static final String KEY_TEST_BATTERY_SAVER_TIP = "test_battery_saver_tip";
private static final String KEY_TEST_HIGH_USAGE_TIP = "test_high_usage_tip";
private static final String KEY_TEST_SMART_BATTERY_TIP = "test_smart_battery_tip";
+ private static final String KEY_TEST_LOW_BATTERY_TIP = "test_low_battery_tip";
/**
* {@code true} if general battery tip is enabled
@@ -192,6 +193,14 @@
*/
public final boolean testSmartBatteryTip;
+ /**
+ * {@code true} if we want to test low battery tip.
+ *
+ * @see Settings.Global#BATTERY_TIP_CONSTANTS
+ * @see #KEY_TEST_LOW_BATTERY_TIP
+ */
+ public final boolean testLowBatteryTip;
+
private final KeyValueListParser mParser;
public BatteryTipPolicy(Context context) {
@@ -221,14 +230,15 @@
appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
- lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, false);
- lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 16);
+ lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, true);
+ lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 3);
dataHistoryRetainDay = mParser.getInt(KEY_DATA_HISTORY_RETAIN_DAY, 30);
excessiveBgDrainPercentage = mParser.getInt(KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE, 10);
testBatterySaverTip = mParser.getBoolean(KEY_TEST_BATTERY_SAVER_TIP, false);
testHighUsageTip = mParser.getBoolean(KEY_TEST_HIGH_USAGE_TIP, false);
testSmartBatteryTip = mParser.getBoolean(KEY_TEST_SMART_BATTERY_TIP, false);
+ testLowBatteryTip = mParser.getBoolean(KEY_TEST_LOW_BATTERY_TIP, false);
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index be25518..9e64c71 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -94,6 +94,7 @@
case BatteryTip.TipType.SMART_BATTERY_MANAGER:
return new SmartBatteryAction(settingsActivity, fragment);
case BatteryTip.TipType.BATTERY_SAVER:
+ case BatteryTip.TipType.LOW_BATTERY:
if (batteryTip.getState() == BatteryTip.StateType.HANDLED) {
return new OpenBatterySaverAction(settingsActivity);
} else {
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
index 2a6302e..c3f9b07 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -16,31 +16,52 @@
package com.android.settings.fuelgauge.batterytip.detectors;
-import android.text.format.DateUtils;
+import android.content.Context;
+import android.os.PowerManager;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
+import java.util.concurrent.TimeUnit;
+
/**
* Detect whether the battery is too low
*/
public class LowBatteryDetector implements BatteryTipDetector {
private BatteryInfo mBatteryInfo;
private BatteryTipPolicy mPolicy;
+ private PowerManager mPowerManager;
+ private int mWarningLevel;
- public LowBatteryDetector(BatteryTipPolicy policy, BatteryInfo batteryInfo) {
+ public LowBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo) {
mPolicy = policy;
mBatteryInfo = batteryInfo;
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWarningLevel = context.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
}
@Override
public BatteryTip detect() {
- // Show it if battery life is less than mPolicy.lowBatteryHour
- final boolean isShown = mPolicy.lowBatteryEnabled && mBatteryInfo.discharging
- && mBatteryInfo.remainingTimeUs < mPolicy.lowBatteryHour * DateUtils.HOUR_IN_MILLIS;
+ final boolean powerSaveModeOn = mPowerManager.isPowerSaveMode();
+ final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel
+ || (mBatteryInfo.discharging
+ && mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(mPolicy.lowBatteryHour));
+
+ int state = BatteryTip.StateType.INVISIBLE;
+ if (mPolicy.lowBatteryEnabled) {
+ if (powerSaveModeOn) {
+ // Show it is handled if battery saver is on
+ state = BatteryTip.StateType.HANDLED;
+ } else if (mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery)) {
+ // Show it is new if in test or in discharging low battery state
+ state = BatteryTip.StateType.NEW;
+ }
+ }
+
return new LowBatteryTip(
- isShown ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE);
+ state, powerSaveModeOn, mBatteryInfo.remainingLabel);
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index f02dd72..2afdc81 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -74,10 +74,10 @@
TIP_ORDER.append(TipType.APP_RESTRICTION, 0);
TIP_ORDER.append(TipType.BATTERY_SAVER, 1);
TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 2);
- TIP_ORDER.append(TipType.SUMMARY, 3);
- TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 4);
- TIP_ORDER.append(TipType.REDUCED_BATTERY, 5);
- TIP_ORDER.append(TipType.LOW_BATTERY, 6);
+ TIP_ORDER.append(TipType.LOW_BATTERY, 3);
+ TIP_ORDER.append(TipType.SUMMARY, 4);
+ TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 5);
+ TIP_ORDER.append(TipType.REDUCED_BATTERY, 6);
TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 7);
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
index 86237dd..b48a7dd 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -25,36 +25,32 @@
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
- * Tip to show current battery life is short
+ * Tip to show current battery level is low or remaining time is less than a certain period
*/
-public class LowBatteryTip extends BatteryTip {
+public class LowBatteryTip extends EarlyWarningTip {
+ private CharSequence mSummary;
- public LowBatteryTip(@StateType int state) {
- super(TipType.LOW_BATTERY, state, false /* showDialog */);
+ public LowBatteryTip(@StateType int state, boolean powerSaveModeOn, CharSequence summary) {
+ super(state, powerSaveModeOn);
+ mType = TipType.LOW_BATTERY;
+ mSummary = summary;
}
- private LowBatteryTip(Parcel in) {
+ public LowBatteryTip(Parcel in) {
super(in);
- }
-
- @Override
- public CharSequence getTitle(Context context) {
- return context.getString(R.string.battery_tip_low_battery_title);
+ mSummary = in.readCharSequence();
}
@Override
public CharSequence getSummary(Context context) {
- return context.getString(R.string.battery_tip_low_battery_summary);
+ return mState == StateType.HANDLED ? context.getString(
+ R.string.battery_tip_early_heads_up_done_summary) : mSummary;
}
@Override
- public int getIconId() {
- return R.drawable.ic_perm_device_information_red_24dp;
- }
-
- @Override
- public void updateState(BatteryTip tip) {
- mState = tip.mState;
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeCharSequence(mSummary);
}
@Override
diff --git a/src/com/android/settings/gestures/SwipeUpPreferenceController.java b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
index 8afd13b..27c7241 100644
--- a/src/com/android/settings/gestures/SwipeUpPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
@@ -24,6 +24,8 @@
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
+import com.android.settings.R;
+
public class SwipeUpPreferenceController extends GesturePreferenceController {
private final int ON = 1;
@@ -39,6 +41,10 @@
}
static boolean isGestureAvailable(Context context) {
+ if (!context.getResources().getBoolean(R.bool.config_swipe_up_gesture_setting_available)) {
+ return false;
+ }
+
final ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP)
@@ -74,8 +80,10 @@
@Override
public boolean isChecked() {
+ final int defaultValue = mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default) ? ON : OFF;
final int swipeUpEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, OFF);
+ Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultValue);
return swipeUpEnabled != OFF;
}
}
diff --git a/src/com/android/settings/notification/ZenModeCallsSettings.java b/src/com/android/settings/notification/ZenModeCallsSettings.java
index 20396b5..cd56d7b 100644
--- a/src/com/android/settings/notification/ZenModeCallsSettings.java
+++ b/src/com/android/settings/notification/ZenModeCallsSettings.java
@@ -16,6 +16,8 @@
package com.android.settings.notification;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+
import android.content.Context;
import android.provider.SearchIndexableResource;
@@ -40,8 +42,8 @@
Lifecycle lifecycle) {
List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeCallsPreferenceController(context, lifecycle));
- // TODO: is a controller needed for a pref that just launches an external activity?
- // or can the contacts app insert this setting themselves?
+ controllers.add(new ZenModeStarredContactsPreferenceController(context, lifecycle,
+ PRIORITY_CATEGORY_CALLS));
controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle,
context.getResources().getInteger(com.android.internal.R.integer
.config_zen_repeat_callers_threshold)));
diff --git a/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java b/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
index c2508e3..95b9f7e 100644
--- a/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
+++ b/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
@@ -16,6 +16,8 @@
package com.android.settings.notification;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+
import android.content.Context;
import android.provider.SearchIndexableResource;
@@ -42,6 +44,8 @@
controllers.add(new ZenModeEventsPreferenceController(context, lifecycle));
controllers.add(new ZenModeRemindersPreferenceController(context, lifecycle));
controllers.add(new ZenModeMessagesPreferenceController(context, lifecycle));
+ controllers.add(new ZenModeStarredContactsPreferenceController(context, lifecycle,
+ PRIORITY_CATEGORY_MESSAGES));
controllers.add(new ZenModeBehaviorFooterPreferenceController(context, lifecycle,
R.string.zen_msg_event_reminder_footer));
return controllers;
diff --git a/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java
new file mode 100644
index 0000000..1b8a3d5
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.icu.text.ListFormatter;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeStarredContactsPreferenceController extends
+ AbstractZenModePreferenceController implements Preference.OnPreferenceClickListener {
+
+ protected static final String KEY = "zen_mode_starred_contacts";
+ private Preference mPreference;
+ private final int mPriorityCategory;
+ private final PackageManager mPackageManager;
+
+ @VisibleForTesting
+ Intent mStarredContactsIntent;
+ @VisibleForTesting
+ Intent mFallbackIntent;
+
+ public ZenModeStarredContactsPreferenceController(Context context, Lifecycle lifecycle, int
+ priorityCategory) {
+ super(context, KEY, lifecycle);
+ mPriorityCategory = priorityCategory;
+ mPackageManager = mContext.getPackageManager();
+
+ mStarredContactsIntent = new Intent(Contacts.Intents.UI.LIST_STARRED_ACTION);
+
+ mFallbackIntent = new Intent(Intent.ACTION_MAIN);
+ mFallbackIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(KEY);
+ mPreference.setOnPreferenceClickListener(this);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mPriorityCategory == PRIORITY_CATEGORY_CALLS) {
+ return mBackend.isPriorityCategoryEnabled(PRIORITY_CATEGORY_CALLS)
+ && mBackend.getPriorityCallSenders() == PRIORITY_SENDERS_STARRED
+ && isIntentValid();
+ } else if (mPriorityCategory == PRIORITY_CATEGORY_MESSAGES) {
+ return mBackend.isPriorityCategoryEnabled(PRIORITY_CATEGORY_MESSAGES)
+ && mBackend.getPriorityMessageSenders() == PRIORITY_SENDERS_STARRED
+ && isIntentValid();
+ } else {
+ // invalid category
+ return false;
+ }
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+
+ List<String> starredContacts = getStarredContacts();
+ int numStarredContacts = starredContacts.size();
+
+ List<String> displayContacts = new ArrayList<>();
+
+ if (numStarredContacts == 0) {
+ displayContacts.add(mContext.getString(R.string.zen_mode_from_none));
+ } else {
+ for (int i = 0; i < 2 && i < numStarredContacts; i++) {
+ displayContacts.add(starredContacts.get(i));
+ }
+
+ if (numStarredContacts == 3) {
+ displayContacts.add(starredContacts.get(2));
+ } else if (numStarredContacts > 2) {
+ displayContacts.add(mContext.getResources().getQuantityString(
+ R.plurals.zen_mode_starred_contacts_summary_additional_contacts,
+ numStarredContacts - 2, numStarredContacts - 2));
+ }
+ }
+
+ mPreference.setSummary(ListFormatter.getInstance().format(displayContacts));
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (mStarredContactsIntent.resolveActivity(mPackageManager) != null) {
+ mContext.startActivity(mStarredContactsIntent);
+ } else {
+ mContext.startActivity(mFallbackIntent);
+ }
+ return true;
+ }
+
+ private List<String> getStarredContacts() {
+ List<String> starredContacts = new ArrayList<>();
+
+ Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
+ ContactsContract.Data.STARRED + "=1", null,
+ ContactsContract.Data.TIMES_CONTACTED);
+
+ if (cursor.moveToFirst()) {
+ do {
+ starredContacts.add(cursor.getString(0));
+ } while (cursor.moveToNext());
+ }
+ return starredContacts;
+ }
+
+ private boolean isIntentValid() {
+ return mStarredContactsIntent.resolveActivity(mPackageManager) != null
+ || mFallbackIntent.resolveActivity(mPackageManager) != null;
+ }
+}
diff --git a/src/com/android/settings/search/DeviceIndexFeatureProvider.java b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
index 37978df..a171844 100644
--- a/src/com/android/settings/search/DeviceIndexFeatureProvider.java
+++ b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
@@ -47,17 +47,19 @@
List<String> keywords);
default void updateIndex(Context context, boolean force) {
- if (!isIndexingEnabled()) return;
+ if (!isIndexingEnabled()) {
+ return;
+ }
if (!force && Objects.equals(
- Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION)) {
+ Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION)) {
// No need to update.
return;
}
- ComponentName jobComponent = new ComponentName(context.getPackageName(),
+ final ComponentName jobComponent = new ComponentName(context.getPackageName(),
DeviceIndexUpdateJobService.class.getName());
- int jobId = context.getResources().getInteger(R.integer.device_index_update);
+ final int jobId = context.getResources().getInteger(R.integer.device_index_update);
// Schedule a job so that we know it'll be able to complete, but try to run as
// soon as possible.
context.getSystemService(JobScheduler.class).schedule(
@@ -70,11 +72,10 @@
Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION);
}
- static String createDeepLink(String s) {
+ static Uri createDeepLink(String s) {
return new Uri.Builder().scheme(SETTINGS)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendQueryParameter(INTENT, s)
- .build()
- .toString();
+ .build();
}
}
diff --git a/src/com/android/settings/search/DeviceIndexUpdateJobService.java b/src/com/android/settings/search/DeviceIndexUpdateJobService.java
index 573dcdf..12a9cf0 100644
--- a/src/com/android/settings/search/DeviceIndexUpdateJobService.java
+++ b/src/com/android/settings/search/DeviceIndexUpdateJobService.java
@@ -17,7 +17,6 @@
import static android.app.slice.Slice.HINT_LARGE;
import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
-
import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
import android.app.job.JobParameters;
@@ -73,16 +72,20 @@
@VisibleForTesting
protected void updateIndex(JobParameters params) {
- if (DEBUG) Log.d(TAG, "Starting index");
- DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(
- this).getDeviceIndexFeatureProvider();
- SliceManager manager = getSliceManager();
- Uri baseUri = new Builder()
+ if (DEBUG) {
+ Log.d(TAG, "Starting index");
+ }
+ final DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(this)
+ .getDeviceIndexFeatureProvider();
+ final SliceManager manager = getSliceManager();
+ final Uri baseUri = new Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.build();
- Collection<Uri> slices = manager.getSliceDescendants(baseUri);
- if (DEBUG) Log.d(TAG, "Indexing " + slices.size() + " slices");
+ final Collection<Uri> slices = manager.getSliceDescendants(baseUri);
+ if (DEBUG) {
+ Log.d(TAG, "Indexing " + slices.size() + " slices");
+ }
for (Uri slice : slices) {
if (!mRunningJob) {
@@ -93,16 +96,20 @@
SliceMetadata metaData = getMetadata(loadedSlice);
CharSequence title = findTitle(loadedSlice, metaData);
if (title != null) {
- if (DEBUG) Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice);
- indexProvider.index(this, title, slice, Uri.parse(createDeepLink(
+ if (DEBUG) {
+ Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice);
+ }
+ indexProvider.index(this, title, slice, createDeepLink(
new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE)
.setPackage(getPackageName())
.putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE, slice.toString())
- .toUri(Intent.URI_ANDROID_APP_SCHEME))),
+ .toUri(Intent.URI_ANDROID_APP_SCHEME)),
metaData.getSliceKeywords());
}
}
- if (DEBUG) Log.d(TAG, "Done indexing");
+ if (DEBUG) {
+ Log.d(TAG, "Done indexing");
+ }
jobFinished(params, false);
}
diff --git a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
index 55448e2..22742e5 100644
--- a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
+++ b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
@@ -23,6 +23,7 @@
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
@@ -32,10 +33,14 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
public class VisiblePatternProfilePreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnResume {
private static final String KEY_VISIBLE_PATTERN_PROFILE = "visiblepattern_profile";
+ private static final String TAG = "VisPtnProfPrefCtrl";
private final LockPatternUtils mLockPatternUtils;
private final UserManager mUm;
@@ -45,7 +50,7 @@
private Preference mPreference;
public VisiblePatternProfilePreferenceController(Context context) {
- this(context, null /* lifecycle */);
+ this(context, null /* lifecycle */);
}
// TODO (b/73074893) Replace this constructor without Lifecycle using setter method instead.
@@ -63,12 +68,25 @@
@Override
public int getAvailabilityStatus() {
- if (mLockPatternUtils.isSecure(mProfileChallengeUserId)
- && mLockPatternUtils.getKeyguardStoredPasswordQuality(mProfileChallengeUserId)
- == PASSWORD_QUALITY_SOMETHING) {
- return AVAILABLE;
+ final FutureTask<Integer> futureTask = new FutureTask<>(
+ // Put the API call in a future to avoid StrictMode violation.
+ () -> {
+ final boolean isSecure = mLockPatternUtils.isSecure(mProfileChallengeUserId);
+ final boolean hasPassword = mLockPatternUtils
+ .getKeyguardStoredPasswordQuality(mProfileChallengeUserId)
+ == PASSWORD_QUALITY_SOMETHING;
+ if (isSecure && hasPassword) {
+ return AVAILABLE;
+ }
+ return DISABLED_FOR_USER;
+ });
+ try {
+ futureTask.run();
+ return futureTask.get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "Error getting lock pattern state.");
+ return DISABLED_FOR_USER;
}
- return DISABLED_FOR_USER;
}
@Override
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 6d6c0ec..af165da 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -26,7 +26,6 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiManager;
-import android.os.StrictMode;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting;
@@ -155,11 +154,6 @@
@Override
public Slice onBindSlice(Uri sliceUri) {
- // TODO: Remove this when all slices are not breaking strict mode
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .permitAll()
- .build());
-
String path = sliceUri.getPath();
// If adding a new Slice, do not directly match Slice URIs.
// Use {@link SlicesDatabaseAccessor}.
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 4e9ee4e..81479c9 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -16,8 +16,12 @@
package com.android.settings.sound;
-
import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
import android.bluetooth.BluetoothDevice;
@@ -38,19 +42,22 @@
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
-import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
+import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -65,12 +72,12 @@
private static final int INVALID_INDEX = -1;
+ protected final List<BluetoothDevice> mConnectedDevices;
protected final AudioManager mAudioManager;
protected final MediaRouter mMediaRouter;
protected final LocalBluetoothProfileManager mProfileManager;
protected int mSelectedIndex;
protected Preference mPreference;
- protected List<BluetoothDevice> mConnectedDevices;
private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
private final LocalBluetoothManager mLocalBluetoothManager;
@@ -89,6 +96,7 @@
mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
mReceiver = new WiredHeadsetBroadcastReceiver();
mMediaRouterCallback = new MediaRouterCallback();
+ mConnectedDevices = new ArrayList<>();
}
/**
@@ -195,12 +203,105 @@
}
protected boolean isStreamFromOutputDevice(int streamType, int device) {
- return mAudioManager.getDevicesForStream(streamType) == device;
+ return (device & mAudioManager.getDevicesForStream(streamType)) != 0;
+ }
+
+ /**
+ * get hands free profile(HFP) connected device
+ */
+ protected List<BluetoothDevice> getConnectedHfpDevices() {
+ final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ final HeadsetProfile hfpProfile = mProfileManager.getHeadsetProfile();
+ if (hfpProfile == null) {
+ return connectedDevices;
+ }
+ final List<BluetoothDevice> devices = hfpProfile.getConnectedDevices();
+ for (BluetoothDevice device : devices) {
+ if (device.isConnected()) {
+ connectedDevices.add(device);
+ }
+ }
+ return connectedDevices;
+ }
+
+ /**
+ * get A2dp connected device
+ */
+ protected List<BluetoothDevice> getConnectedA2dpDevices() {
+ final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile == null) {
+ return connectedDevices;
+ }
+ final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
+ for (BluetoothDevice device : devices) {
+ if (device.isConnected()) {
+ connectedDevices.add(device);
+ }
+ }
+ return connectedDevices;
+ }
+
+ /**
+ * get hearing aid profile connected device, exclude other devices with same hiSyncId.
+ */
+ protected List<BluetoothDevice> getConnectedHearingAidDevices() {
+ final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ if (hapProfile == null) {
+ return connectedDevices;
+ }
+ final List<Long> devicesHiSyncIds = new ArrayList<>();
+ final List<BluetoothDevice> devices = hapProfile.getConnectedDevices();
+ for (BluetoothDevice device : devices) {
+ final long hiSyncId = hapProfile.getHiSyncId(device);
+ // device with same hiSyncId should not be shown in the UI.
+ // So do not add it into connectedDevices.
+ if (!devicesHiSyncIds.contains(hiSyncId) && device.isConnected()) {
+ devicesHiSyncIds.add(hiSyncId);
+ connectedDevices.add(device);
+ }
+ }
+ return connectedDevices;
+ }
+
+ /**
+ * According to different stream and output device, find the active device from
+ * the corresponding profile. Hearing aid device could stream both STREAM_MUSIC
+ * and STREAM_VOICE_CALL.
+ *
+ * @param streamType the type of audio streams.
+ * @return the active device. Return null if the active device is current device
+ * or streamType is not STREAM_MUSIC or STREAM_VOICE_CALL.
+ */
+ protected BluetoothDevice findActiveDevice(int streamType) {
+ if (streamType != STREAM_MUSIC && streamType != STREAM_VOICE_CALL) {
+ return null;
+ }
+ if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_ALL_A2DP)) {
+ return mProfileManager.getA2dpProfile().getActiveDevice();
+ } else if (isStreamFromOutputDevice(STREAM_VOICE_CALL, DEVICE_OUT_ALL_SCO)) {
+ return mProfileManager.getHeadsetProfile().getActiveDevice();
+ } else if (isStreamFromOutputDevice(streamType, DEVICE_OUT_HEARING_AID)) {
+ // The first element is the left active device; the second element is
+ // the right active device. And they will have same hiSyncId. If either
+ // or both side is not active, it will be null on that position.
+ List<BluetoothDevice> activeDevices =
+ mProfileManager.getHearingAidProfile().getActiveDevices();
+ for (BluetoothDevice btDevice : activeDevices) {
+ if (btDevice != null && mConnectedDevices.contains(btDevice)) {
+ // also need to check mConnectedDevices, because one of
+ // the device(same hiSyncId) might not be shown in the UI.
+ return btDevice;
+ }
+ }
+ }
+ return null;
}
int getDefaultDeviceIndex() {
// Default device is after all connected devices.
- return ArrayUtils.size(mConnectedDevices);
+ return mConnectedDevices.size();
}
void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
@@ -261,7 +362,7 @@
mContext.unregisterReceiver(mReceiver);
}
- /** Callback for headset plugged and unplugged events. */
+ /** Notifications of audio device connection and disconnection events. */
private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
diff --git a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
index 6907a4a..2039913 100644
--- a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
+++ b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.sound;
+import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.media.AudioManager.STREAM_VOICE_CALL;
import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
@@ -25,12 +26,12 @@
import android.content.Context;
import android.support.v7.preference.Preference;
-import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
/**
- * This class allows switching between HFP-connected BT devices
+ * This class allows switching between HFP-connected & HAP-connected BT devices
* while in on-call state.
*/
public class HandsFreeProfileOutputPreferenceController extends
@@ -57,16 +58,11 @@
// Ongoing call status, list all the connected devices support hands free profile.
// Select current active device.
// Disable switch entry if there is no connected device.
- mConnectedDevices = null;
- BluetoothDevice activeDevice = null;
+ mConnectedDevices.clear();
+ mConnectedDevices.addAll(getConnectedHfpDevices());
+ mConnectedDevices.addAll(getConnectedHearingAidDevices());
- final HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
- if (headsetProfile != null) {
- mConnectedDevices = headsetProfile.getConnectedDevices();
- activeDevice = headsetProfile.getActiveDevice();
- }
-
- final int numDevices = ArrayUtils.size(mConnectedDevices);
+ final int numDevices = mConnectedDevices.size();
if (numDevices == 0) {
// No connected devices, disable switch entry.
mPreference.setVisible(false);
@@ -79,7 +75,7 @@
CharSequence[] mediaValues = new CharSequence[numDevices + 1];
// Setup devices entries, select active connected device
- setupPreferenceEntries(mediaOutputs, mediaValues, activeDevice);
+ setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice(STREAM_VOICE_CALL));
if (isStreamFromOutputDevice(STREAM_VOICE_CALL, DEVICE_OUT_USB_HEADSET)) {
// If wired headset is plugged in and active, select to default device.
@@ -92,8 +88,21 @@
@Override
public void setActiveBluetoothDevice(BluetoothDevice device) {
- if (Utils.isAudioModeOngoingCall(mContext)) {
- mProfileManager.getHeadsetProfile().setActiveDevice(device);
+ if (!Utils.isAudioModeOngoingCall(mContext)) {
+ return;
+ }
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ final HeadsetProfile hfpProfile = mProfileManager.getHeadsetProfile();
+ if (hapProfile != null && hfpProfile != null && device == null) {
+ hfpProfile.setActiveDevice(null);
+ hapProfile.setActiveDevice(null);
+ return;
+ }
+ if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
+ hapProfile.setActiveDevice(device);
+ }
+ if (hfpProfile != null) {
+ hfpProfile.setActiveDevice(device);
}
}
}
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index d0677a7..79f3c9d 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.sound;
+import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
@@ -27,13 +28,12 @@
import android.media.AudioManager;
import android.support.v7.preference.Preference;
-import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settingslib.bluetooth.A2dpProfile;
-
+import com.android.settingslib.bluetooth.HearingAidProfile;
/**
- * This class which allows switching between a2dp-connected BT devices.
+ * This class which allows switching between A2dp-connected & HAP-connected BT devices.
* A few conditions will disable this switcher:
* - No available BT device(s)
* - Media stream captured by cast device
@@ -67,18 +67,14 @@
return;
}
+ mConnectedDevices.clear();
// Otherwise, list all of the A2DP connected device and display the active device.
- mConnectedDevices = null;
- BluetoothDevice activeDevice = null;
if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
- final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
- if (a2dpProfile != null) {
- mConnectedDevices = a2dpProfile.getConnectedDevices();
- activeDevice = a2dpProfile.getActiveDevice();
- }
+ mConnectedDevices.addAll(getConnectedA2dpDevices());
+ mConnectedDevices.addAll(getConnectedHearingAidDevices());
}
- final int numDevices = ArrayUtils.size(mConnectedDevices);
+ final int numDevices = mConnectedDevices.size();
if (numDevices == 0) {
// Disable switch entry if there is no connected devices.
mPreference.setVisible(false);
@@ -91,7 +87,7 @@
CharSequence[] mediaValues = new CharSequence[numDevices + 1];
// Setup devices entries, select active connected device
- setupPreferenceEntries(mediaOutputs, mediaValues, activeDevice);
+ setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice(STREAM_MUSIC));
if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_USB_HEADSET)) {
// If wired headset is plugged in and active, select to default device.
@@ -104,8 +100,21 @@
@Override
public void setActiveBluetoothDevice(BluetoothDevice device) {
- if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
- mProfileManager.getA2dpProfile().setActiveDevice(device);
+ if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
+ return;
+ }
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (hapProfile != null && a2dpProfile != null && device == null) {
+ hapProfile.setActiveDevice(null);
+ a2dpProfile.setActiveDevice(null);
+ return;
+ }
+ if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
+ hapProfile.setActiveDevice(device);
+ }
+ if (a2dpProfile != null) {
+ a2dpProfile.setActiveDevice(device);
}
}
}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
index 7bc76a2..88715bf 100644
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.java
@@ -35,6 +35,9 @@
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
public class SystemUpdatePreferenceController extends BasePreferenceController {
private static final String TAG = "SysUpdatePrefContr";
@@ -84,9 +87,19 @@
@Override
public CharSequence getSummary() {
- final Bundle updateInfo = mUpdateManager.retrieveSystemUpdateInfo();
CharSequence summary = mContext.getString(R.string.android_version_summary,
Build.VERSION.RELEASE);
+ final FutureTask<Bundle> bundleFutureTask = new FutureTask<>(
+ // Put the API call in a future to avoid StrictMode violation.
+ () -> mUpdateManager.retrieveSystemUpdateInfo());
+ final Bundle updateInfo;
+ try {
+ bundleFutureTask.run();
+ updateInfo = bundleFutureTask.get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "Error getting system update info.");
+ return summary;
+ }
switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
case SystemUpdateManager.STATUS_IN_PROGRESS:
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index f710613..4f7b3ba 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -106,7 +106,7 @@
void startTether() {
mSwitchBar.setEnabled(false);
- mConnectivityManager.startTethering(TETHERING_WIFI, false /* showProvisioningUi */,
+ mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
}
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java b/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
index 8df4a31..e3f9092 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
@@ -22,6 +22,7 @@
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.drawer.CategoryKey;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
@@ -45,4 +46,10 @@
assertThat(indexedXml).contains(R.xml.night_display_settings);
}
+
+ @Test
+ public void getCategoryKey_isCategoryNightLight() {
+ NightDisplaySettings settings = new NightDisplaySettings();
+ assertThat(settings.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_NIGHT_LIGHT);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 5c2a1c7..4fd3a82 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -612,6 +612,14 @@
}
@Test
+ public void testShouldHideAnomaly_AppInDozeList_returnTrue() {
+ doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
+ doReturn(true).when(mPowerWhitelistBackend).isWhitelisted(new String[]{HIGH_SDK_PACKAGE});
+
+ assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, UID)).isTrue();
+ }
+
+ @Test
public void testShouldHideAnomaly_normalApp_returnFalse() {
doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
index f159e79..8df1f21 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
@@ -46,8 +46,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.Process;
import android.os.StatsDimensionsValue;
import android.os.UserManager;
@@ -57,11 +55,11 @@
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.overlay.FeatureFactory;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
+import com.android.settings.testutils.shadow.ShadowPowerWhitelistBackend;
import org.junit.Before;
import org.junit.Test;
@@ -80,7 +78,7 @@
import java.util.concurrent.TimeUnit;
@RunWith(SettingsRobolectricTestRunner.class)
-@Config(shadows = ShadowConnectivityManager.class)
+@Config(shadows = {ShadowConnectivityManager.class, ShadowPowerWhitelistBackend.class})
public class AnomalyDetectionJobServiceTest {
private static final int UID = 12345;
private static final String SYSTEM_PACKAGE = "com.android.system";
@@ -91,8 +89,6 @@
private static final int ANOMALY_TYPE = 6;
private static final long VERSION_CODE = 15;
@Mock
- private BatteryStatsHelper mBatteryStatsHelper;
- @Mock
private UserManager mUserManager;
@Mock
private BatteryDatabaseManager mBatteryDatabaseManager;
@@ -148,9 +144,9 @@
@Test
public void saveAnomalyToDatabase_systemWhitelisted_doNotSave() {
doReturn(UID).when(mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
- doReturn(true).when(mPowerWhitelistBackend).isSysWhitelistedExceptIdle(any(String[].class));
+ doReturn(true).when(mPowerWhitelistBackend).isWhitelisted(any(String[].class));
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider,
@@ -171,7 +167,7 @@
mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
doReturn(true).when(mBatteryUtils).shouldHideAnomaly(any(), anyInt());
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider,
@@ -191,7 +187,7 @@
doReturn(Process.SYSTEM_UID).when(
mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -206,7 +202,7 @@
doReturn(AnomalyDetectionJobService.UID_NULL).when(
mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -226,7 +222,7 @@
doReturn(Process.FIRST_APPLICATION_UID).when(
mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -251,7 +247,7 @@
doReturn(Process.FIRST_APPLICATION_UID).when(
mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
- mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+ mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
mPowerWhitelistBackend, mContext.getContentResolver(),
mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -316,4 +312,12 @@
// Should not crash even job is stopped
mAnomalyDetectionJobService.completeWork(mJobParameters, mJobWorkItem);
}
+
+ @Test
+ public void restartWorkAfterBeenStopped_jobStarted() {
+ mAnomalyDetectionJobService.onStopJob(mJobParameters);
+ mAnomalyDetectionJobService.onStartJob(mJobParameters);
+
+ assertThat(mAnomalyDetectionJobService.mIsJobCanceled).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index 7a41921..a02bdc2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -113,7 +113,7 @@
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
- mContext.getString(R.string.battery_tip_dialog_message, 1, "60 minutes ago"));
+ mContext.getString(R.string.battery_tip_dialog_message, 1));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index b50cc40..b0d6a7d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -48,9 +48,9 @@
BatteryTip.TipType.APP_RESTRICTION,
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.HIGH_DEVICE_USAGE,
+ BatteryTip.TipType.LOW_BATTERY,
BatteryTip.TipType.SUMMARY,
- BatteryTip.TipType.SMART_BATTERY_MANAGER,
- BatteryTip.TipType.LOW_BATTERY};
+ BatteryTip.TipType.SMART_BATTERY_MANAGER};
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index 654b247..547e0eb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -48,7 +48,8 @@
+ ",excessive_bg_drain_percentage=25"
+ ",test_battery_saver_tip=true"
+ ",test_high_usage_tip=false"
- + ",test_smart_battery_tip=true";
+ + ",test_smart_battery_tip=true"
+ + ",test_low_battery_tip=true";
private Context mContext;
@Before
@@ -80,6 +81,7 @@
assertThat(batteryTipPolicy.testBatterySaverTip).isTrue();
assertThat(batteryTipPolicy.testHighUsageTip).isFalse();
assertThat(batteryTipPolicy.testSmartBatteryTip).isTrue();
+ assertThat(batteryTipPolicy.testLowBatteryTip).isTrue();
}
@Test
@@ -99,12 +101,13 @@
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
- assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse();
- assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(16);
+ assertThat(batteryTipPolicy.lowBatteryEnabled).isTrue();
+ assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(3);
assertThat(batteryTipPolicy.dataHistoryRetainDay).isEqualTo(30);
assertThat(batteryTipPolicy.excessiveBgDrainPercentage).isEqualTo(10);
assertThat(batteryTipPolicy.testBatterySaverTip).isFalse();
assertThat(batteryTipPolicy.testHighUsageTip).isFalse();
assertThat(batteryTipPolicy.testSmartBatteryTip).isFalse();
+ assertThat(batteryTipPolicy.testLowBatteryTip).isFalse();
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
index 7a25555..459c4e2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
@@ -29,6 +29,7 @@
import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -51,6 +52,7 @@
private InstrumentedPreferenceFragment mFragment;
private RestrictAppTip mRestrictAppTip;
private EarlyWarningTip mEarlyWarningTip;
+ private LowBatteryTip mLowBatteryTip;
@Before
public void setUp() {
@@ -59,7 +61,11 @@
FakeFeatureFactory.setupForTest();
doReturn(RuntimeEnvironment.application).when(mFragment).getContext();
mRestrictAppTip = spy(new RestrictAppTip(BatteryTip.StateType.NEW, new ArrayList<>()));
- mEarlyWarningTip = spy(new EarlyWarningTip(BatteryTip.StateType.NEW, true));
+ mEarlyWarningTip = spy(
+ new EarlyWarningTip(BatteryTip.StateType.NEW, true /* powerSaveModeOn */));
+ mLowBatteryTip = spy(
+ new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+ "" /* summary */));
}
@Test
@@ -93,4 +99,20 @@
assertThat(BatteryTipUtils.getActionForBatteryTip(mEarlyWarningTip, mSettingsActivity,
mFragment)).isInstanceOf(OpenBatterySaverAction.class);
}
+
+ @Test
+ public void testGetActionForBatteryTip_typeLowBatteryStateNew_returnActionBatterySaver() {
+ when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.NEW);
+
+ assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
+ mFragment)).isInstanceOf(BatterySaverAction.class);
+ }
+
+ @Test
+ public void testGetActionForBatteryTip_typeLowBatteryStateHandled_returnActionOpen() {
+ when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.HANDLED);
+
+ assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
+ mFragment)).isInstanceOf(OpenBatterySaverAction.class);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
index 1f4affa..9764559 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -17,12 +17,15 @@
package com.android.settings.fuelgauge.batterytip.detectors;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
-import android.text.format.DateUtils;
+import android.content.Context;
+import android.os.PowerManager;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -31,8 +34,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPowerManager;
import org.robolectric.util.ReflectionHelpers;
+import java.util.concurrent.TimeUnit;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class LowBatteryDetectorTest {
@@ -40,36 +47,68 @@
private BatteryInfo mBatteryInfo;
private BatteryTipPolicy mPolicy;
private LowBatteryDetector mLowBatteryDetector;
+ private ShadowPowerManager mShadowPowerManager;
+ private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application));
+ mContext = RuntimeEnvironment.application;
+ mShadowPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class));
ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true);
+ ReflectionHelpers.setField(mPolicy, "lowBatteryHour", 3);
+ mBatteryInfo.discharging = true;
- mLowBatteryDetector = new LowBatteryDetector(mPolicy, mBatteryInfo);
+ mLowBatteryDetector = new LowBatteryDetector(mContext, mPolicy, mBatteryInfo);
}
@Test
public void testDetect_disabledByPolicy_tipInvisible() {
ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false);
+ mShadowPowerManager.setIsPowerSaveMode(true);
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
}
@Test
- public void testDetect_shortBatteryLife_tipVisible() {
- mBatteryInfo.discharging = true;
- mBatteryInfo.remainingTimeUs = DateUtils.MINUTE_IN_MILLIS;
+ public void testDetect_enabledByTest_tipNew() {
+ ReflectionHelpers.setField(mPolicy, "testLowBatteryTip", true);
- assertThat(mLowBatteryDetector.detect().isVisible()).isTrue();
+ assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
}
@Test
- public void testDetect_longBatteryLife_tipInvisible() {
- mBatteryInfo.discharging = true;
- mBatteryInfo.remainingTimeUs = DateUtils.DAY_IN_MILLIS;
+ public void testDetect_lowBattery_tipNew() {
+ mBatteryInfo.batteryLevel = 3;
+ mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1);
+ assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
+
+ mBatteryInfo.batteryLevel = 50;
+ mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
+ assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
+ }
+
+ @Test
+ public void testDetect_batterySaverOn_tipHandled() {
+ mShadowPowerManager.setIsPowerSaveMode(true);
+
+ assertThat(mLowBatteryDetector.detect().getState())
+ .isEqualTo(BatteryTip.StateType.HANDLED);
+ }
+
+ @Test
+ public void testDetect_charging_tipInvisible() {
+ mBatteryInfo.discharging = false;
+
+ assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+ }
+
+ @Test
+ public void testDetect_noEarlyWarning_tipInvisible() {
+ mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMicros(1);
+ mBatteryInfo.batteryLevel = 100;
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
new file mode 100644
index 0000000..359d260
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.fuelgauge.batterytip.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.Parcel;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class LowBatteryTipTest {
+
+ private static final CharSequence SUMMARY = "Only 15 minutes left";
+
+ @Mock
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+ private Context mContext;
+ private LowBatteryTip mLowBatteryTip;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mLowBatteryTip = new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+ SUMMARY);
+ }
+
+ @Test
+ public void testParcelable() {
+ Parcel parcel = Parcel.obtain();
+ mLowBatteryTip.writeToParcel(parcel, mLowBatteryTip.describeContents());
+ parcel.setDataPosition(0);
+
+ final LowBatteryTip parcelTip = new LowBatteryTip(parcel);
+
+ assertThat(parcelTip.isPowerSaveModeOn()).isFalse();
+ assertThat(parcelTip.getSummary(mContext)).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void getSummary_tipHandled_showSummary() {
+ mLowBatteryTip.mState = BatteryTip.StateType.HANDLED;
+
+ assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo("Some features may be limited");
+ }
+
+ @Test
+ public void getSummary_tipNew_showSummary() {
+ mLowBatteryTip.mState = BatteryTip.StateType.NEW;
+
+ assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void log_lowBatteryActionWithCorrectState() {
+ mLowBatteryTip.log(mContext, mMetricsFeatureProvider);
+
+ verify(mMetricsFeatureProvider).action(mContext,
+ MetricsProto.MetricsEvent.ACTION_LOW_BATTERY_TIP, BatteryTip.StateType.NEW);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/GestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/GestureSettingsTest.java
new file mode 100644
index 0000000..29d2c19
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/gestures/GestureSettingsTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.gestures;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.drawer.CategoryKey;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class GestureSettingsTest {
+
+ @Test
+ public void getCategoryKey_isCategoryGestures() {
+ GestureSettings settings = new GestureSettings();
+ assertThat(settings.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_GESTURES);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
index 360609b..4107b73 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
@@ -18,35 +18,32 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
import android.os.UserManager;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import org.junit.After;
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.RuntimeEnvironment;
import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;
-import java.util.ArrayList;
-import java.util.List;
-
@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = SettingsShadowResources.class)
public class SwipeUpPreferenceControllerTest {
private Context mContext;
@@ -58,11 +55,21 @@
@Before
public void setUp() {
+ SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available,
+ true);
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_swipe_up_gesture_default, true);
+
mContext = RuntimeEnvironment.application;
mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
mController = new SwipeUpPreferenceController(mContext, KEY_SWIPE_UP);
}
+ @After
+ public void tearDown() {
+ SettingsShadowResources.reset();
+ }
+
@Test
public void testIsGestureAvailable_matchingServiceExists_shouldReturnTrue() {
final ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -75,19 +82,45 @@
}
@Test
+ public void testIsGestureAvailable_overlayDisabled_matchingServiceExists_shouldReturnFalse() {
+ SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available,
+ false);
+
+ final ComponentName recentsComponentName = ComponentName.unflattenFromString(
+ mContext.getString(com.android.internal.R.string.config_recentsComponentName));
+ final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP)
+ .setPackage(recentsComponentName.getPackageName());
+ mPackageManager.addResolveInfoForIntent(quickStepIntent, new ResolveInfo());
+
+ assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse();
+ }
+
+ @Test
public void testIsGestureAvailable_noMatchingServiceExists_shouldReturnFalse() {
assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse();
}
@Test
- public void testIsChecked_configIsSet_shouldReturnTrue() {
+ public void testIsChecked_defaultIsTrue_shouldReturnTrue() {
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void testIsChecked_defaultIsFalse_shouldReturnFalse() {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_swipe_up_gesture_default, false);
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void testIsChecked_setCheckedTrue_shouldReturnTrue() {
// Set the setting to be enabled.
mController.setChecked(true);
assertThat(mController.isChecked()).isTrue();
}
@Test
- public void testIsChecked_configIsNotSet_shouldReturnFalse() {
+ public void testIsChecked_setCheckedFalse_shouldReturnFalse() {
// Set the setting to be disabled.
mController.setChecked(false);
assertThat(mController.isChecked()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java
new file mode 100644
index 0000000..7a45d7f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ZenModeStarredContactsPreferenceControllerTest {
+
+ private ZenModeStarredContactsPreferenceController mCallsController;
+ private ZenModeStarredContactsPreferenceController mMessagesController;
+
+ @Mock
+ private ZenModeBackend mBackend;
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Mock
+ private ListPreference mockPref;
+ @Mock
+ private NotificationManager.Policy mPolicy;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private Intent testIntent;
+ @Mock
+ private ComponentName mComponentName;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+ mContext = shadowApplication.getApplicationContext();
+ when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+ when(testIntent.resolveActivity(any())).thenReturn(mComponentName);
+
+ mCallsController = new ZenModeStarredContactsPreferenceController(
+ mContext, mock(Lifecycle.class), PRIORITY_CATEGORY_CALLS);
+ ReflectionHelpers.setField(mCallsController, "mBackend", mBackend);
+ ReflectionHelpers.setField(mCallsController, "mStarredContactsIntent", testIntent);
+ when(mPreferenceScreen.findPreference(mCallsController.getPreferenceKey()))
+ .thenReturn(mockPref);
+ mCallsController.displayPreference(mPreferenceScreen);
+
+ mMessagesController = new ZenModeStarredContactsPreferenceController(
+ mContext, mock(Lifecycle.class), PRIORITY_CATEGORY_MESSAGES);
+ ReflectionHelpers.setField(mMessagesController, "mBackend", mBackend);
+ ReflectionHelpers.setField(mMessagesController, "mStarredContactsIntent", testIntent);
+ when(mPreferenceScreen.findPreference(mMessagesController.getPreferenceKey()))
+ .thenReturn(mockPref);
+ mMessagesController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void isAvailable_noCallers() {
+ when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+ .thenReturn(false);
+ assertThat(mCallsController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_anyCallers() {
+ when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+ .thenReturn(true);
+ when(mBackend.getPriorityCallSenders())
+ .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+
+
+ assertThat(mCallsController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_starredCallers() {
+ when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+ .thenReturn(true);
+ when(mBackend.getPriorityCallSenders())
+ .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+
+ assertThat(mCallsController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_noMessages() {
+ when(mBackend.isPriorityCategoryEnabled(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(false);
+ assertThat(mMessagesController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_anyMessages() {
+ when(mBackend.isPriorityCategoryEnabled(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(true);
+ when(mBackend.getPriorityMessageSenders())
+ .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+
+ assertThat(mMessagesController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_starredMessageContacts() {
+ when(mBackend.isPriorityCategoryEnabled(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(true);
+ when(mBackend.getPriorityMessageSenders())
+ .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+
+ assertThat(mMessagesController.isAvailable()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 0fda33e..a0bd33a 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -18,20 +18,17 @@
package com.android.settings.slices;
import static android.content.ContentResolver.SCHEME_CONTENT;
-
import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.slice.SliceManager;
-import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
+import android.os.StrictMode;
import android.provider.SettingsSlicesContract;
import com.android.settings.testutils.DatabaseTestUtils;
@@ -44,29 +41,30 @@
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
-import androidx.slice.Slice;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import androidx.slice.Slice;
+
/**
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
*/
@RunWith(SettingsRobolectricTestRunner.class)
public class SettingsSliceProviderTest {
- private final String KEY = "KEY";
- private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
- private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
- private final String TITLE = "title";
- private final String SUMMARY = "summary";
- private final String SCREEN_TITLE = "screen title";
- private final String FRAGMENT_NAME = "fragment name";
- private final int ICON = 1234; // I declare a thumb war
- private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
- private final String PREF_CONTROLLER = FakeToggleController.class.getName();
+ private static final String KEY = "KEY";
+ private static final String INTENT_PATH =
+ SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
+ private static final String TITLE = "title";
+ private static final String SUMMARY = "summary";
+ private static final String SCREEN_TITLE = "screen title";
+ private static final String FRAGMENT_NAME = "fragment name";
+ private static final int ICON = 1234; // I declare a thumb war
+ private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
+ private static final String PREF_CONTROLLER = FakeToggleController.class.getName();
+
private Context mContext;
private SettingsSliceProvider mProvider;
private SQLiteDatabase mDb;
@@ -152,6 +150,18 @@
}
@Test
+ public void onBindSlice_shouldNotOverrideStrictMode() {
+ final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
+ SliceData data = getDummyData();
+ mProvider.mSliceWeakDataCache.put(data.getUri(), data);
+ mProvider.onBindSlice(data.getUri());
+
+ final StrictMode.ThreadPolicy newThreadPolicy = StrictMode.getThreadPolicy();
+
+ assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
+ }
+
+ @Test
public void testLoadSlice_cachedEntryRemovedOnUnpin() {
SliceData data = getDummyData();
mProvider.mSliceDataCache.put(data.getUri(), data);
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index cc1785b..272fd20 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -17,6 +17,15 @@
package com.android.settings.sound;
+import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
+import static android.media.AudioSystem.STREAM_MUSIC;
+
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
@@ -28,6 +37,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
@@ -48,7 +59,8 @@
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -59,7 +71,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -77,19 +88,24 @@
private static final String TEST_KEY = "Test_Key";
private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
- private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
- private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+ private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+ private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+ private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+ private final static long HISYNCID1 = 10;
+ private final static long HISYNCID2 = 11;
@Mock
private LocalBluetoothManager mLocalManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock
private A2dpProfile mA2dpProfile;
+ @Mock
+ private HeadsetProfile mHeadsetProfile;
+ @Mock
+ private HearingAidProfile mHearingAidProfile;
private Context mContext;
private PreferenceScreen mScreen;
@@ -99,10 +115,13 @@
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mBluetoothDevice;
- private ShadowBluetoothDevice mShadowBluetoothDevice;
+ private BluetoothDevice mLeftBluetoothHapDevice;
+ private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
private AudioSwitchPreferenceController mController;
- private List<BluetoothDevice> mConnectedDevices;
+ private List<BluetoothDevice> mProfileConnectedDevices;
+ private List<BluetoothDevice> mHearingAidActiveDevices;
+ private List<BluetoothDevice> mEmptyDevices;
@Before
public void setUp() {
@@ -118,20 +137,27 @@
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+ when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile);
mBluetoothManager = new BluetoothManager(mContext);
mBluetoothAdapter = mBluetoothManager.getAdapter();
- mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
- mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
- mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+ when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+ when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+ mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+ when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
mController = new AudioSwitchPreferenceControllerTestable(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
mPreference = new ListPreference(mContext);
- mConnectedDevices = new ArrayList<>(1);
- mConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices = new ArrayList<>();
+ mHearingAidActiveDevices = new ArrayList<>(2);
+ mEmptyDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mScreen.getContext()).thenReturn(mContext);
@@ -179,7 +205,8 @@
@Test
public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
- mController.mConnectedDevices = mConnectedDevices;
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
mController.onPreferenceChange(mPreference,
mContext.getText(R.string.media_output_default_summary));
@@ -194,7 +221,8 @@
*/
@Test
public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
- mController.mConnectedDevices = mConnectedDevices;
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
@@ -210,12 +238,11 @@
ShadowBluetoothDevice shadowBluetoothDevice;
BluetoothDevice secondBluetoothDevice;
secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
- shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
+ shadowBluetoothDevice = shadowOf(secondBluetoothDevice);
shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
- List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
- connectedDevices.add(mBluetoothDevice);
- connectedDevices.add(secondBluetoothDevice);
- mController.mConnectedDevices = connectedDevices;
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(secondBluetoothDevice);
mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
@@ -223,16 +250,263 @@
}
/**
- * mConnectedDevices is Null.
+ * mConnectedDevices is empty.
* onPreferenceChange should return false.
*/
@Test
public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
- mController.mConnectedDevices = null;
+ mController.mConnectedDevices.clear();
assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
}
+ /**
+ * Audio stream output to bluetooth sco headset which is the subset of all sco device.
+ * isStreamFromOutputDevice should return true.
+ */
+ @Test
+ public void isStreamFromOutputDevice_outputDeviceIsBtScoHeadset_shouldReturnTrue() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+
+ assertThat(mController.isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_ALL_SCO)).isTrue();
+ }
+
+ /**
+ * Audio stream is not STREAM_MUSIC or STREAM_VOICE_CALL.
+ * findActiveDevice should return null.
+ */
+ @Test
+ public void findActiveDevice_streamIsRing_shouldReturnNull() {
+ assertThat(mController.findActiveDevice(STREAM_RING)).isNull();
+ }
+
+ /**
+ * Audio stream is STREAM_MUSIC and output device is A2dp bluetooth device.
+ * findActiveDevice should return A2dp active device.
+ */
+ @Test
+ public void findActiveDevice_streamMusicToA2dpDevice_shouldReturnActiveA2dpDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mLeftBluetoothHapDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mBluetoothDevice);
+ }
+
+ /**
+ * Audio stream is STREAM_VOICE_CALL and output device is Hands free profile bluetooth device.
+ * findActiveDevice should return Hands free profile active device.
+ */
+ @Test
+ public void findActiveDevice_streamVoiceCallToHfpDevice_shouldReturnActiveHfpDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(mBluetoothDevice);
+ }
+
+ /**
+ * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid profile
+ * bluetooth device. And left side of HAP device is active.
+ * findActiveDevice should return hearing aid device active device.
+ */
+ @Test
+ public void findActiveDevice_streamToHapDeviceLeftActiveDevice_shouldReturnActiveHapDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(null);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mLeftBluetoothHapDevice);
+ assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+ mLeftBluetoothHapDevice);
+ }
+
+ /**
+ * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid profile
+ * bluetooth device. And right side of HAP device is active.
+ * findActiveDevice should return hearing aid device active device.
+ */
+ @Test
+ public void findActiveDevice_streamToHapDeviceRightActiveDevice_shouldReturnActiveHapDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(null);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mRightBluetoothHapDevice);
+ assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+ mRightBluetoothHapDevice);
+ }
+
+ /**
+ * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid
+ * profile bluetooth device. And both are active device.
+ * findActiveDevice should return only return the active device in mConnectedDevices.
+ */
+ @Test
+ public void findActiveDevice_streamToHapDeviceTwoActiveDevice_shouldReturnActiveHapDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mRightBluetoothHapDevice);
+ assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+ mRightBluetoothHapDevice);
+ }
+
+ /**
+ * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid
+ * profile bluetooth device. And none of them are active.
+ * findActiveDevice should return null.
+ */
+ @Test
+ public void findActiveDevice_streamToOtherDevice_shouldReturnActiveHapDevice() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mController.findActiveDevice(STREAM_MUSIC)).isNull();
+ assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isNull();
+ }
+
+ /**
+ * Two hearing aid devices with different HisyncId
+ * getConnectedHearingAidDevices should add both device to list.
+ */
+ @Test
+ public void getConnectedHearingAidDevices_deviceHisyncIdIsDifferent_shouldAddBothToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+ mEmptyDevices.addAll(mController.getConnectedHearingAidDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mLeftBluetoothHapDevice,
+ mRightBluetoothHapDevice);
+ }
+
+ /**
+ * Two hearing aid devices with same HisyncId
+ * getConnectedHearingAidDevices should only add first device to list.
+ */
+ @Test
+ public void getConnectedHearingAidDevices_deviceHisyncIdIsSame_shouldAddOneToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mEmptyDevices.addAll(mController.getConnectedHearingAidDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mLeftBluetoothHapDevice);
+ }
+
+ /**
+ * One A2dp device is connected.
+ * getConnectedA2dpDevices should add this device to list.
+ */
+ @Test
+ public void getConnectedA2dpDevices_oneConnectedA2dpDevice_shouldAddDeviceToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+ mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
+ }
+
+ /**
+ * More than one A2dp devices are connected.
+ * getConnectedA2dpDevices should add all devices to list.
+ */
+ @Test
+ public void getConnectedA2dpDevices_moreThanOneConnectedA2dpDevice_shouldAddDeviceToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+ mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
+ }
+
+ /**
+ * One hands free profile device is connected.
+ * getConnectedA2dpDevices should add this device to list.
+ */
+ @Test
+ public void getConnectedHfpDevices_oneConnectedHfpDevice_shouldAddDeviceToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+ mEmptyDevices.addAll(mController.getConnectedHfpDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
+ }
+
+ /**
+ * More than one hands free profile devices are connected.
+ * getConnectedA2dpDevices should add all devices to list.
+ */
+ @Test
+ public void getConnectedHfpDevices_moreThanOneConnectedHfpDevice_shouldAddDeviceToList() {
+ mEmptyDevices.clear();
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+ mEmptyDevices.addAll(mController.getConnectedHfpDevices());
+
+ assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
+ }
+
private class AudioSwitchPreferenceControllerTestable extends
AudioSwitchPreferenceController {
AudioSwitchPreferenceControllerTestable(Context context, String key) {
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index 0692c9c..db09eab 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -17,12 +17,16 @@
package com.android.settings.sound;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,8 +45,8 @@
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowMediaRouter;
import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -53,7 +57,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -69,21 +72,27 @@
)
public class HandsFreeProfileOutputPreferenceControllerTest {
private static final String TEST_KEY = "Test_Key";
- private static final String TEST_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
- private static final String TEST_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
- private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
- private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+ private static final String TEST_DEVICE_NAME_1 = "Test_HFP_BT_Device_NAME_1";
+ private static final String TEST_DEVICE_NAME_2 = "Test_HFP_BT_Device_NAME_2";
+ private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
+ private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
+ private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+ private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+ private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+ private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
+ private final static long HISYNCID1 = 10;
+ private final static long HISYNCID2 = 11;
@Mock
private LocalBluetoothManager mLocalManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock
private HeadsetProfile mHeadsetProfile;
+ @Mock
+ private HearingAidProfile mHearingAidProfile;
private Context mContext;
private PreferenceScreen mScreen;
@@ -93,10 +102,13 @@
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mBluetoothDevice;
- private ShadowBluetoothDevice mShadowBluetoothDevice;
+ private BluetoothDevice mSecondBluetoothDevice;
+ private BluetoothDevice mLeftBluetoothHapDevice;
+ private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
private AudioSwitchPreferenceController mController;
- private List<BluetoothDevice> mConnectedDevices;
+ private List<BluetoothDevice> mProfileConnectedDevices;
+ private List<BluetoothDevice> mHearingAidActiveDevices;
@Before
public void setUp() {
@@ -112,19 +124,32 @@
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile);
+ when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
mBluetoothManager = new BluetoothManager(mContext);
mBluetoothAdapter = mBluetoothManager.getAdapter();
- mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
- mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
- mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+ when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+ when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
+
+ mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+ when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
+ when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+
+ mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
+ when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
+ when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
mController = new HandsFreeProfileOutputPreferenceController(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
mPreference = new ListPreference(mContext);
- mConnectedDevices = new ArrayList<>(1);
- mConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices = new ArrayList<>();
+ mHearingAidActiveDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mScreen.getContext()).thenReturn(mContext);
@@ -140,8 +165,26 @@
ShadowBluetoothUtils.reset();
}
+ /**
+ * During a call, bluetooth device with HisyncId.
+ * HearingAidProfile should set active device to this device.
+ */
@Test
- public void setActiveBluetoothDevice_duringACalling_shouldSetBtDeviceActive() {
+ public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
+
+ verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
+ }
+
+ /**
+ * During a call, Bluetooth device without HisyncId.
+ * HeadsetProfile should set active device to this device.
+ */
+ @Test
+ public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
mController.setActiveBluetoothDevice(mBluetoothDevice);
@@ -149,23 +192,60 @@
verify(mHeadsetProfile).setActiveDevice(mBluetoothDevice);
}
+ /**
+ * During a call, set active device to "this device".
+ * HeadsetProfile should set to null.
+ * HearingAidProfile should set to null.
+ */
+ @Test
+ public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+ mController.setActiveBluetoothDevice(null);
+
+ verify(mHeadsetProfile).setActiveDevice(null);
+ verify(mHearingAidProfile).setActiveDevice(null);
+ }
+
+ /**
+ * In normal mode
+ * HeadsetProfile should not set active device.
+ */
+ @Test
+ public void setActiveBluetoothDevice_inNormalMode_shouldNotSetActiveDeviceToHeadsetProfile() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+ mController.setActiveBluetoothDevice(mBluetoothDevice);
+
+ verify(mHeadsetProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+ }
+
+ /**
+ * Default status
+ * Preference should be invisible
+ * Summary should be default summary
+ */
@Test
public void updateState_shouldSetSummary() {
mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isFalse();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getText(R.string.media_output_default_summary));
}
/**
- * One Headset Bluetooth device is available and activated
+ * One Hands Free Profile Bluetooth device is available and activated
* Preference should be visible
- * Preference summary should be activate device name
+ * Preference summary should be the activated device name
*/
@Test
public void updateState_oneHeadsetsAvailableAndActivated_shouldSetDeviceName() {
mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
mController.updateState(mPreference);
@@ -175,29 +255,25 @@
}
/**
- * More than one Headset Bluetooth devices are available, and second device is active.
+ * More than one Hands Free Profile Bluetooth devices are available, and second
+ * device is active.
* Preference should be visible
- * Preference summary should be activate device name
+ * Preference summary should be the activated device name
*/
@Test
- public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- ShadowBluetoothDevice shadowBluetoothDevice;
+ public void updateState_moreThanOneHfpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- BluetoothDevice secondBluetoothDevice;
- secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
- shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
- shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
connectedDevices.add(mBluetoothDevice);
- connectedDevices.add(secondBluetoothDevice);
-
+ connectedDevices.add(mSecondBluetoothDevice);
when(mHeadsetProfile.getConnectedDevices()).thenReturn(connectedDevices);
- when(mHeadsetProfile.getActiveDevice()).thenReturn(secondBluetoothDevice);
+ when(mHeadsetProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(secondBluetoothDevice.getName());
+ assertThat(mPreference.getSummary()).isEqualTo(mSecondBluetoothDevice.getName());
}
/**
@@ -209,8 +285,10 @@
@Test
public void updateState_withAvailableDevicesWiredHeadsetActivated_shouldSetDefaultSummary() {
mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- mShadowAudioManager.setStream(DEVICE_OUT_USB_HEADSET);
- when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_USB_HEADSET);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mHeadsetProfile.getActiveDevice()).thenReturn(
mBluetoothDevice); // BT device is still activated in this case
@@ -238,4 +316,150 @@
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getText(R.string.media_output_default_summary));
}
+
+ /**
+ * One hearing aid profile Bluetooth device is available and active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ */
+ @Test
+ public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+ }
+
+ /**
+ * More than one hearing aid profile Bluetooth devices are available, and second
+ * device is active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ */
+ @Test
+ public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ }
+
+ /**
+ * Both hearing aid profile and hands free profile Bluetooth devices are available, and
+ * two hearing aid profile devices with same HisyncId. Both of HAP device are active,
+ * "left" side HAP device is added first.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should not contain second HAP device with same HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ //with same HisyncId, only the first one will remain in UI.
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+ assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
+ assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
+ }
+
+ /**
+ * Both hearing aid profile and hands free profile Bluetooth devices are available, and
+ * two hearing aid profile devices with same HisyncId. Both of HAP device are active,
+ * "right" side HAP device is added first.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should not contain second HAP device with same HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ //with same HisyncId, only the first one will remain in UI.
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
+ assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ }
+
+ /**
+ * Both hearing aid profile and hands free profile Bluetooth devices are available, and
+ * two hearing aid profile devices with different HisyncId. One of HAP device is active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should contain both HAP device with different HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(null);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
+ mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index b62e6b3..b777239 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -17,13 +17,17 @@
package com.android.settings.sound;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,7 +47,7 @@
import com.android.settings.testutils.shadow.ShadowMediaRouter;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -54,7 +58,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -72,19 +75,25 @@
private static final String TEST_KEY = "Test_Key";
private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
- private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
- private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+ private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
+ private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
+ private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+ private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+ private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+ private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
+ private final static long HISYNCID1 = 10;
+ private final static long HISYNCID2 = 11;
@Mock
private LocalBluetoothManager mLocalManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock
private A2dpProfile mA2dpProfile;
+ @Mock
+ private HearingAidProfile mHearingAidProfile;
private Context mContext;
private PreferenceScreen mScreen;
@@ -94,10 +103,13 @@
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mBluetoothDevice;
- private ShadowBluetoothDevice mShadowBluetoothDevice;
+ private BluetoothDevice mSecondBluetoothDevice;
+ private BluetoothDevice mLeftBluetoothHapDevice;
+ private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
private AudioSwitchPreferenceController mController;
- private List<BluetoothDevice> mConnectedDevices;
+ private List<BluetoothDevice> mProfileConnectedDevices;
+ private List<BluetoothDevice> mHearingAidActiveDevices;
@Before
public void setUp() {
@@ -113,19 +125,32 @@
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
mBluetoothManager = new BluetoothManager(mContext);
mBluetoothAdapter = mBluetoothManager.getAdapter();
- mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
- mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
- mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+ when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+ when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
+
+ mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+ when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
+ when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+
+ mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
+ when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
+ when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
mPreference = new ListPreference(mContext);
- mConnectedDevices = new ArrayList<>(1);
- mConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices = new ArrayList<>();
+ mHearingAidActiveDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mScreen.getContext()).thenReturn(mContext);
@@ -141,8 +166,26 @@
ShadowBluetoothUtils.reset();
}
+ /**
+ * In normal mode, bluetooth device with HisyncId.
+ * HearingAidProfile should set active device to this device.
+ */
@Test
- public void setActiveBluetoothDevice_withoutRingAndCall_shouldSetBtDeviceActive() {
+ public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
+
+ verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
+ }
+
+ /**
+ * In normal mode, bluetooth device without HisyncId.
+ * A2dpProfile should set active device to this device.
+ */
+ @Test
+ public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
mController.setActiveBluetoothDevice(mBluetoothDevice);
@@ -150,16 +193,50 @@
verify(mA2dpProfile).setActiveDevice(mBluetoothDevice);
}
+ /**
+ * In normal mode, set active device to "this device".
+ * A2dpProfile should set to null.
+ * HearingAidProfile should set to null.
+ */
+ @Test
+ public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+ mController.setActiveBluetoothDevice(null);
+
+ verify(mA2dpProfile).setActiveDevice(null);
+ verify(mHearingAidProfile).setActiveDevice(null);
+ }
+
+ /**
+ * During a call
+ * A2dpProfile should not set active device.
+ */
+ @Test
+ public void setActiveBluetoothDevice_duringACall_shouldNotSetActiveDeviceToA2dpProfile() {
+ mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+ mController.setActiveBluetoothDevice(mBluetoothDevice);
+
+ verify(mA2dpProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+ }
+
+ /**
+ * Default status
+ * Preference should be invisible
+ * Summary should be default summary
+ */
@Test
public void updateState_shouldSetSummary() {
mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isFalse();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getText(R.string.media_output_default_summary));
}
/**
- * On going call state:
+ * During a call
* Preference should be invisible
* Default string should be "Unavailable during calls"
*/
@@ -199,7 +276,7 @@
*/
@Test
public void updateState_mediaStreamIsCapturedByCast_shouldDisableAndSetDefaultSummary() {
- mShadowAudioManager.setStream(DEVICE_OUT_REMOTE_SUBMIX);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_REMOTE_SUBMIX);
mController.updateState(mPreference);
@@ -211,12 +288,15 @@
/**
* One A2DP Bluetooth device is available and active.
* Preference should be visible
- * Preference summary should be activate device name
+ * Preference summary should be the activated device name
*/
@Test
public void updateState_oneA2dpBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
mController.updateState(mPreference);
@@ -228,27 +308,22 @@
/**
* More than one A2DP Bluetooth devices are available, and second device is active.
* Preference should be visible
- * Preference summary should be activate device name
+ * Preference summary should be the activated device name
*/
@Test
public void updateState_moreThanOneA2DpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- ShadowBluetoothDevice shadowBluetoothDevice;
mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
- BluetoothDevice secondBluetoothDevice;
- secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
- shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
- shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
- List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
- connectedDevices.add(mBluetoothDevice);
- connectedDevices.add(secondBluetoothDevice);
-
- when(mA2dpProfile.getConnectedDevices()).thenReturn(connectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(secondBluetoothDevice);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(secondBluetoothDevice.getName());
+ assertThat(mPreference.getSummary()).isEqualTo(mSecondBluetoothDevice.getName());
}
/**
@@ -259,16 +334,18 @@
@Test
public void updateState_a2dpDevicesAvailableWiredHeadsetIsActivated_shouldSetDefaultSummary() {
mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setStream(DEVICE_OUT_USB_HEADSET);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_USB_HEADSET);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mA2dpProfile.getActiveDevice()).thenReturn(
mBluetoothDevice); // BT device is still activated in this case
mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isTrue();
- String defaultString = mContext.getString(R.string.media_output_default_summary);
- assertThat(mPreference.getSummary()).isEqualTo(defaultString);
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getString(R.string.media_output_default_summary));
}
@@ -280,13 +357,161 @@
@Test
public void updateState_a2dpDevicesAvailableCurrentDeviceActivated_shouldSetDefaultSummary() {
mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mA2dpProfile.getActiveDevice()).thenReturn(null);
mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isTrue();
- String defaultString = mContext.getString(R.string.media_output_default_summary);
- assertThat(mPreference.getSummary()).isEqualTo(defaultString);
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getString(R.string.media_output_default_summary));
+ }
+
+ /**
+ * One hearing aid profile Bluetooth device is available and active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ */
+ @Test
+ public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+ }
+
+ /**
+ * More than one hearing aid profile Bluetooth devices are available, and second
+ * device is active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ */
+ @Test
+ public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ }
+
+ /**
+ * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+ * profile devices with same HisyncId are active. Both of HAP device are active,
+ * "left" side HAP device is added first.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should not contain second HAP device with same HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ //with same HisyncId, first one will remain in UI.
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+ assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
+ assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
+ }
+
+ /**
+ * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+ * profile devices with same HisyncId. Both of HAP device are active,
+ * "right" side HAP device is added first.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should not contain second HAP device with same HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ //with same HisyncId, first one will remain in UI.
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
+ assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
+ }
+
+ /**
+ * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+ * profile devices with different HisyncId. One of HAP device is active.
+ * Preference should be visible
+ * Preference summary should be the activated device name
+ * ConnectedDevice should contain both HAP device with different HisyncId
+ */
+ @Test
+ public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
+ mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+ mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(null);
+ mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+ assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
+ mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
}
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
index 0de2156..88a0fb6 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
@@ -41,7 +41,7 @@
@Implements(value = AudioManager.class, inheritImplementationMethods = true)
public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManager {
private int mRingerMode;
- private int mStream;
+ private int mDeviceCodes;
private boolean mMusicActiveRemotely = false;
private ArrayList<AudioDeviceCallback> mDeviceCallbacks = new ArrayList();
@@ -79,8 +79,8 @@
return mMusicActiveRemotely;
}
- public void setStream(int stream) {
- mStream = stream;
+ public void setOutputDevice(int deviceCodes) {
+ mDeviceCodes = deviceCodes;
}
@Implementation
@@ -94,7 +94,7 @@
case STREAM_NOTIFICATION:
case STREAM_DTMF:
case STREAM_ACCESSIBILITY:
- return mStream;
+ return mDeviceCodes;
default:
return 0;
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerWhitelistBackend.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerWhitelistBackend.java
new file mode 100644
index 0000000..4500b23
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerWhitelistBackend.java
@@ -0,0 +1,28 @@
+/*
+ * 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.testutils.shadow;
+
+import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
+
+import org.robolectric.annotation.Implements;
+
+@Implements(PowerWhitelistBackend.class)
+public class ShadowPowerWhitelistBackend {
+ public void __constructor__() {
+ // Do nothing
+ }
+}
diff --git a/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java b/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java
new file mode 100644
index 0000000..e7c5d6e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.slices;
+
+import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SliceDeepLinkSpringBoardTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void launcheDeepLinkIntent_shouldNotCrash() {
+ final Uri springBoardIntentUri = createDeepLink(
+ new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE)
+ .setPackage(mContext.getPackageName())
+ .putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE,
+ "content://com.android.settings.slices/action/test_slice")
+ .toUri(Intent.URI_ANDROID_APP_SCHEME));
+
+ final Intent deepLinkIntent = new Intent(Intent.ACTION_VIEW)
+ .setData(springBoardIntentUri)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mContext.startActivity(deepLinkIntent);
+ }
+}