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
This commit is contained in:
24
res/layout/companion_apps_remove_button_widget.xml
Normal file
24
res/layout/companion_apps_remove_button_widget.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentDescription="@string/remove_association_button"
|
||||
android:id="@+id/remove_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_clear"
|
||||
android:background="@android:color/transparent"/>
|
26
res/layout/preference_companion_app.xml
Normal file
26
res/layout/preference_companion_app.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||
</LinearLayout>
|
@@ -205,6 +205,9 @@
|
||||
<dimen name="bluetooth_pairing_edittext_padding">21dp</dimen>
|
||||
<dimen name="bluetooth_checkbox_padding">16dp</dimen>
|
||||
|
||||
<!-- CompanionAppWidgetPreferencce Padding -->
|
||||
<dimen name="bluetooth_companion_app_widget">20dp</dimen>
|
||||
|
||||
<!-- WiFi Preferences -->
|
||||
<dimen name="wifi_divider_height">1px</dimen>
|
||||
<dimen name="wifi_ap_band_checkbox_padding">16dp</dimen>
|
||||
|
@@ -1817,6 +1817,10 @@
|
||||
<string name="bluetooth_device_mac_address">Device\'s Bluetooth address: <xliff:g id="address">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details. The title of a confirmation dialog for unpairing a paired device. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_unpair_dialog_title">Forget device?</string>
|
||||
<!-- Content Description for companion device app associations removal button [CHAR LIMIT=28]-->
|
||||
<string name="remove_association_button">Remove association</string>
|
||||
<!-- Bluetooth device details companion apps. The title of a confirmation dialog for remove an app. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_companion_app_remove_association_dialog_title">Disconnect App?</string>
|
||||
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
|
||||
<string name="bluetooth_unpair_dialog_body" product="default">Your phone will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
@@ -1824,12 +1828,16 @@
|
||||
<string name="bluetooth_unpair_dialog_body" product="tablet">Your tablet will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
|
||||
<string name="bluetooth_unpair_dialog_body" product="device">Your device will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details companion apps. The body of confirmation dialog for remove association. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_companion_app_body"><xliff:g id="app_name" example="App Name">%1$s</xliff:g> app will no longer connect to your <xliff:g id="device_name" example="Device Name">%2$s</xliff:g></string>
|
||||
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_untethered_unpair_dialog_body"><xliff:g id="device_name" example="Jack's headphone">%1$s</xliff:g> will no longer be paired with any device linked to this account</string>
|
||||
|
||||
<!-- Bluetooth device details. In the confirmation dialog for unpairing a paired device, this is the label on the button that will complete the unpairing action. -->
|
||||
<string name="bluetooth_unpair_dialog_forget_confirm_button">Forget device</string>
|
||||
<!-- Bluetooth device details companion apps. In the confirmation dialog for removing an associated app, this is the label on the button that will complete the disassociate action. [CHAR LIMIT=80] -->
|
||||
<string name = "bluetooth_companion_app_remove_association_confirm_button">Disconnect app</string>
|
||||
|
||||
<!-- Bluetooth settings. The title of the screen to pick which profiles to connect to on the device. For example, headphones may have both A2DP and headset, this allows the users to choose which one they want to connect to. -->
|
||||
<string name="bluetooth_connect_specific_profiles_title">Connect to\u2026</string>
|
||||
|
@@ -44,6 +44,9 @@
|
||||
settings:allowDividerBelow="true"
|
||||
settings:allowDividerAbove="true"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="device_companion_apps"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="bluetooth_profiles"/>
|
||||
|
||||
|
@@ -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<Association> 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<String> getPreferencesNeedToShow(String address, PreferenceCategory container) {
|
||||
List<String> preferencesToRemove = new ArrayList<>();
|
||||
Set<String> 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<String> 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;
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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<String> mPackages;
|
||||
private List<CharSequence> mAppNames;
|
||||
private List<Association> 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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<CompanionAppWidgetPreference> 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user