From 7dc2624b51f54689f226a628b16b83bbf372a6eb Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 10 Nov 2020 11:54:39 -0800 Subject: [PATCH] Companion Device App showing in Settings Added a feature that users are able to see/remove the apps' associations in Settings/Connected devices. It will display the icon of the associations app, app's name and a button that users are able to remove the associations. Also it will pop up a dialog alerting user before remove the associations. Screenshot: https://screenshot.googleplex.com/APSRhW2retYmAAK Bug: 165951651 Test: Manually Test Change-Id: Iccaeaf516e8a78d4ef33415c1c2d7379139ec88c --- .../companion_apps_remove_button_widget.xml | 24 ++ res/layout/preference_companion_app.xml | 26 ++ res/values/dimens.xml | 3 + res/values/strings.xml | 8 + res/xml/bluetooth_device_details_fragment.xml | 3 + ...uetoothDetailsCompanionAppsController.java | 224 ++++++++++++++++++ .../BluetoothDeviceDetailsFragment.java | 2 + .../CompanionAppWidgetPreference.java | 58 +++++ ...othDetailsCompanionAppsControllerTest.java | 178 ++++++++++++++ .../CompanionAppWidgetPreferenceTest.java | 103 ++++++++ 10 files changed, 629 insertions(+) create mode 100644 res/layout/companion_apps_remove_button_widget.xml create mode 100644 res/layout/preference_companion_app.xml create mode 100644 src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java create mode 100644 src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java create mode 100644 tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java diff --git a/res/layout/companion_apps_remove_button_widget.xml b/res/layout/companion_apps_remove_button_widget.xml new file mode 100644 index 00000000000..a3c229537ac --- /dev/null +++ b/res/layout/companion_apps_remove_button_widget.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/res/layout/preference_companion_app.xml b/res/layout/preference_companion_app.xml new file mode 100644 index 00000000000..22712753b2f --- /dev/null +++ b/res/layout/preference_companion_app.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 6047092f8d3..74db2323822 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -205,6 +205,9 @@ 21dp 16dp + + 20dp + 1px 16dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 221f049dd4e..9bfb9bd9904 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1817,6 +1817,10 @@ Device\'s Bluetooth address: %1$s Forget device? + + Remove association + + Disconnect App? Your phone will no longer be paired with %1$s @@ -1824,12 +1828,16 @@ Your tablet will no longer be paired with %1$s Your device will no longer be paired with %1$s + + %1$s app will no longer connect to your %2$s %1$s will no longer be paired with any device linked to this account Forget device + + Disconnect app Connect to\u2026 diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index 94052b67213..5084d4d481a 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -44,6 +44,9 @@ settings:allowDividerBelow="true" settings:allowDividerAbove="true"/> + + diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java new file mode 100644 index 00000000000..f2a94ba7440 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2021 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.bluetooth; + +import static com.android.internal.util.CollectionUtils.filter; + +import android.companion.Association; +import android.companion.CompanionDeviceManager; +import android.companion.ICompanionDeviceManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import com.google.common.base.Objects; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + + +/** + * This class adds Companion Device app rows to launch the app or remove the associations + */ +public class BluetoothDetailsCompanionAppsController extends BluetoothDetailsController { + public static final String KEY_DEVICE_COMPANION_APPS = "device_companion_apps"; + private static final String LOG_TAG = "BTCompanionController"; + + private CachedBluetoothDevice mCachedDevice; + + @VisibleForTesting + PreferenceCategory mProfilesContainer; + + @VisibleForTesting + CompanionDeviceManager mCompanionDeviceManager; + + @VisibleForTesting + PackageManager mPackageManager; + + public BluetoothDetailsCompanionAppsController(Context context, + PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + mCachedDevice = device; + mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class); + mPackageManager = context.getPackageManager(); + lifecycle.addObserver(this); + } + + @Override + protected void init(PreferenceScreen screen) { + mProfilesContainer = screen.findPreference(getPreferenceKey()); + mProfilesContainer.setLayoutResource(R.layout.preference_companion_app); + } + + private List getAssociations(String address) { + return filter( + mCompanionDeviceManager.getAllAssociations(), + a -> Objects.equal(address, a.getDeviceMacAddress())); + } + + private static void removePreference(PreferenceCategory container, String packageName) { + Preference preference = container.findPreference(packageName); + if (preference != null) { + container.removePreference(preference); + } + } + + private void removeAssociationDialog(String packageName, String address, + PreferenceCategory container, CharSequence appName, Context context) { + DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> { + if (which == DialogInterface.BUTTON_POSITIVE) { + removeAssociation(packageName, address, container); + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + builder.setPositiveButton( + R.string.bluetooth_companion_app_remove_association_confirm_button, + dialogClickListener) + .setNegativeButton(android.R.string.cancel, dialogClickListener) + .setTitle(R.string.bluetooth_companion_app_remove_association_dialog_title) + .setMessage(mContext.getString( + R.string.bluetooth_companion_app_body, appName, mCachedDevice.getName())) + .show(); + } + + private static void removeAssociation(String packageName, String address, + PreferenceCategory container) { + try { + java.util.Objects.requireNonNull(ICompanionDeviceManager.Stub.asInterface( + ServiceManager.getService( + Context.COMPANION_DEVICE_SERVICE))).disassociate( + address, packageName); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + removePreference(container, packageName); + } + + private CharSequence getAppName(String packageName) { + CharSequence appName = null; + try { + appName = mPackageManager.getApplicationLabel( + mPackageManager.getApplicationInfo(packageName, 0)); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "Package Not Found", e); + } + + return appName; + } + + private List getPreferencesNeedToShow(String address, PreferenceCategory container) { + List preferencesToRemove = new ArrayList<>(); + Set packages = getAssociations(address) + .stream().map(Association::getPackageName) + .collect(Collectors.toSet()); + + for (int i = 0; i < container.getPreferenceCount(); i++) { + String preferenceKey = container.getPreference(i).getKey(); + if (packages.isEmpty() || !packages.contains(preferenceKey)) { + preferencesToRemove.add(preferenceKey); + } + } + + for (String preferenceName : preferencesToRemove) { + removePreference(container, preferenceName); + } + + return packages.stream() + .filter(p -> container.findPreference(p) == null) + .collect(Collectors.toList()); + } + + /** + * Refreshes the state of the preferences for all the associations, possibly adding or + * removing preferences as needed. + */ + @Override + protected void refresh() { + updatePreferences(mContext, mCachedDevice.getAddress(), mProfilesContainer); + } + + /** + * Add preferences for each association for the bluetooth device + */ + public void updatePreferences(Context context, + String address, PreferenceCategory container) { + Set addedPackages = new HashSet<>(); + + for (String packageName : getPreferencesNeedToShow(address, container)) { + CharSequence appName = getAppName(packageName); + + if (TextUtils.isEmpty(appName) || !addedPackages.add(packageName)) { + continue; + } + + Drawable removeIcon = context.getResources().getDrawable(R.drawable.ic_clear); + CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference( + removeIcon, + v -> removeAssociationDialog(packageName, address, container, appName, context), + context + ); + + Drawable appIcon; + + try { + appIcon = mPackageManager.getApplicationIcon(packageName); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "Icon Not Found", e); + continue; + } + Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName); + preference.setIcon(appIcon); + preference.setTitle(appName.toString()); + preference.setOnPreferenceClickListener(v -> { + context.startActivity(intent); + return true; + }); + + preference.setKey(packageName); + preference.setVisible(true); + container.addPreference(preference); + } + } + + @Override + public String getPreferenceKey() { + return KEY_DEVICE_COMPANION_APPS; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index a8812475ace..4980ba313fb 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -185,6 +185,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment lifecycle, mManager)); controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsCompanionAppsController(context, this, + mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsProfilesController(context, this, mManager, mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, diff --git a/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java b/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java new file mode 100644 index 00000000000..cd0643373d6 --- /dev/null +++ b/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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.bluetooth; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.ImageButton; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; + +/** + * A custom preference for companion device apps. Added a button for association removal + */ +public class CompanionAppWidgetPreference extends Preference { + private Drawable mWidgetIcon; + private View.OnClickListener mWidgetListener; + private int mImageButtonPadding; + + public CompanionAppWidgetPreference(Drawable widgetIcon, View.OnClickListener widgetListener, + Context context) { + super(context); + mWidgetIcon = widgetIcon; + mWidgetListener = widgetListener; + mImageButtonPadding = context.getResources().getDimensionPixelSize( + R.dimen.bluetooth_companion_app_widget); + setWidgetLayoutResource(R.layout.companion_apps_remove_button_widget); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + ImageButton imageButton = (ImageButton) holder.findViewById(R.id.remove_button); + imageButton.setPadding( + mImageButtonPadding, mImageButtonPadding, mImageButtonPadding, mImageButtonPadding); + imageButton.setColorFilter(getContext().getColor(android.R.color.darker_gray)); + imageButton.setImageDrawable(mWidgetIcon); + imageButton.setOnClickListener(mWidgetListener); + } + +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java new file mode 100644 index 00000000000..3f49938412b --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2021 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.bluetooth; + +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.companion.Association; +import android.companion.CompanionDeviceManager; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RunWith(RobolectricTestRunner.class) +public class BluetoothDetailsCompanionAppsControllerTest extends + BluetoothDetailsControllerTestBase { + private static final String PACKAGE_NAME_ONE = "com.google.android.deskclock"; + private static final String PACKAGE_NAME_TWO = "com.google.android.calculator"; + private static final String PACKAGE_NAME_THREE = "com.google.android.GoogleCamera"; + private static final CharSequence APP_NAME_ONE = "deskclock"; + private static final CharSequence APP_NAME_TWO = "calculator"; + private static final CharSequence APP_NAME_THREE = "GoogleCamera"; + + @Mock + private CompanionDeviceManager mCompanionDeviceManager; + @Mock + private PackageManager mPackageManager; + + private BluetoothDetailsCompanionAppsController mController; + private PreferenceCategory mProfiles; + private List mPackages; + private List mAppNames; + private List mAssociations; + + + @Override + public void setUp() { + super.setUp(); + mPackages = Arrays.asList(PACKAGE_NAME_ONE, PACKAGE_NAME_TWO, PACKAGE_NAME_THREE); + mAppNames = Arrays.asList(APP_NAME_ONE, APP_NAME_TWO, APP_NAME_THREE); + mProfiles = spy(new PreferenceCategory(mContext)); + mAssociations = new ArrayList<>(); + when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations); + when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager); + setupDevice(mDeviceConfig); + mController = + new BluetoothDetailsCompanionAppsController(mContext, mFragment, mCachedDevice, + mLifecycle); + mController.mCompanionDeviceManager = mCompanionDeviceManager; + mController.mPackageManager = mPackageManager; + mController.mProfilesContainer = mProfiles; + mProfiles.setKey(mController.getPreferenceKey()); + mScreen.addPreference(mProfiles); + } + + private void setupFakeLabelAndInfo(String packageName, CharSequence appName) { + ApplicationInfo appInfo = mock(ApplicationInfo.class); + try { + when(mPackageManager.getApplicationInfo(packageName, 0)).thenReturn(appInfo); + when(mPackageManager.getApplicationLabel(appInfo)).thenReturn(appName); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + } + + private void addFakeAssociation(String packageName, CharSequence appName) { + setupFakeLabelAndInfo(packageName, appName); + Association association = new Association( + 0, mCachedDevice.getAddress(), packageName, "", true, System.currentTimeMillis()); + mAssociations.add(association); + showScreen(mController); + } + + private Preference getPreference(int index) { + PreferenceCategory preferenceCategory = mProfiles.findPreference( + mController.getPreferenceKey()); + return Objects.requireNonNull(preferenceCategory).getPreference(index); + } + + private void removeAssociation(String packageName) { + mAssociations = mAssociations.stream() + .filter(a -> !a.getPackageName().equals(packageName)) + .collect(Collectors.toList()); + + when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations); + + showScreen(mController); + } + + @Test + public void addOneAssociation_preferenceShouldBeAdded() { + addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE); + + Preference preferenceOne = getPreference(0); + + assertThat(preferenceOne.getClass()).isEqualTo(CompanionAppWidgetPreference.class); + assertThat(preferenceOne.getKey()).isEqualTo(PACKAGE_NAME_ONE); + assertThat(preferenceOne.getTitle()).isEqualTo(APP_NAME_ONE.toString()); + assertThat(mProfiles.getPreferenceCount()).isEqualTo(1); + + removeAssociation(PACKAGE_NAME_ONE); + + assertThat(mProfiles.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void removeOneAssociation_preferenceShouldBeRemoved() { + addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE); + + removeAssociation(PACKAGE_NAME_ONE); + + assertThat(mProfiles.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void addMultipleAssociations_preferencesShouldBeAdded() { + for (int i = 0; i < mPackages.size(); i++) { + addFakeAssociation(mPackages.get(i), mAppNames.get(i)); + + Preference preference = getPreference(i); + + assertThat(preference.getClass()).isEqualTo(CompanionAppWidgetPreference.class); + assertThat(preference.getKey()).isEqualTo(mPackages.get(i)); + assertThat(preference.getTitle()).isEqualTo(mAppNames.get(i).toString()); + assertThat(mProfiles.getPreferenceCount()).isEqualTo(i + 1); + } + } + + @Test + public void removeMultipleAssociations_preferencesShouldBeRemoved() { + for (int i = 0; i < mPackages.size(); i++) { + addFakeAssociation(mPackages.get(i), mAppNames.get(i).toString()); + } + + for (int i = 0; i < mPackages.size(); i++) { + removeAssociation(mPackages.get(i)); + + assertThat(mProfiles.getPreferenceCount()).isEqualTo(mPackages.size() - i - 1); + + if (i == mPackages.size() - 1) { + break; + } + + assertThat(getPreference(0).getKey()).isEqualTo(mPackages.get(i + 1)); + assertThat(getPreference(0).getTitle()).isEqualTo(mAppNames.get(i + 1).toString()); + } + } +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java new file mode 100644 index 00000000000..8ee3ef78d81 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.ContextThemeWrapper; +import android.view.View; + +import com.android.settings.R; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowAlertDialogCompat.class}) +public class CompanionAppWidgetPreferenceTest { + private static final String TITLE_ONE = "Test Title 1"; + private static final String TITLE_TWO = "Test Title 1"; + private static final String KEY_ONE = "Test Key 1"; + private static final String KEY_TWO = "Test Key 1"; + + private Context mContext; + private Drawable mWidgetIconOne; + private Drawable mWidgetIconTwo; + private Drawable mAppIconOne; + private Drawable mAppIconTwo; + + @Mock + private View.OnClickListener mWidgetListenerOne; + @Mock + private View.OnClickListener mWidgetListenerTwo; + + private List mPreferenceContainer; + + @Before + public void setUp() { + mPreferenceContainer = new ArrayList<>(); + Context context = spy(RuntimeEnvironment.application.getApplicationContext()); + mContext = new ContextThemeWrapper(context, R.style.Theme_Settings); + mWidgetIconOne = mock(Drawable.class); + mAppIconOne = mock(Drawable.class); + mWidgetListenerOne = mock(View.OnClickListener.class); + mWidgetIconTwo = mock(Drawable.class); + mAppIconTwo = mock(Drawable.class); + mWidgetListenerTwo = mock(View.OnClickListener.class); + } + + private void setUpPreferenceContainer(Drawable widgetIcon, Drawable appIcon, + View.OnClickListener listener, String title, String key) { + CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference( + widgetIcon, listener, mContext); + preference.setIcon(appIcon); + preference.setTitle(title); + preference.setKey(key); + mPreferenceContainer.add(preference); + } + + @Test + public void setUpPreferenceContainer_preferenceShouldBeAdded() { + setUpPreferenceContainer( + mWidgetIconOne, mAppIconOne, mWidgetListenerOne, TITLE_ONE, KEY_ONE); + + assertThat(mPreferenceContainer.get(0).getIcon()).isEqualTo(mAppIconOne); + assertThat(mPreferenceContainer.get(0).getKey()).isEqualTo(KEY_ONE); + assertThat(mPreferenceContainer.get(0).getTitle()).isEqualTo(TITLE_ONE); + + setUpPreferenceContainer( + mWidgetIconTwo, mAppIconTwo, mWidgetListenerTwo, TITLE_TWO, KEY_TWO); + + assertThat(mPreferenceContainer.get(1).getIcon()).isEqualTo(mAppIconTwo); + assertThat(mPreferenceContainer.get(1).getKey()).isEqualTo(KEY_TWO); + assertThat(mPreferenceContainer.get(1).getTitle()).isEqualTo(TITLE_TWO); + } +}