Replace the SlicePreference with Preference
The Settings' 2 panel did not support SlicePreference, since it always open activity with NEW_TASK and it casues the settings can't set new page at right side. Bug: 270544054 Test: build pass. local test: the phone pair the buds with fastpair, and then check the slice preferences. atest BlockingPrefWithSliceControllerTest (pass) Change-Id: I0e8abfd284492f04ab322a5bed13741fc6b25b34
This commit is contained in:
@@ -491,6 +491,15 @@
|
|||||||
<!-- Slice Uri to query nearby devices. -->
|
<!-- Slice Uri to query nearby devices. -->
|
||||||
<string name="config_nearby_devices_slice_uri" translatable="false">content://com.google.android.gms.nearby.fastpair/device_status_list_item</string>
|
<string name="config_nearby_devices_slice_uri" translatable="false">content://com.google.android.gms.nearby.fastpair/device_status_list_item</string>
|
||||||
|
|
||||||
|
<!-- BT Slice intent action. To support Settings 2 panel, BT slice can't use PendingIntent.send(). Therefore, here defines the Slice intent action. -->
|
||||||
|
<string name="config_bt_slice_intent_action" translatable="false"></string>
|
||||||
|
<!-- BT Slice pending intent action. To support Settings 2 panel, BT slice can't use PendingIntent.send(). Therefore, here defines the Slice pending intent action. -->
|
||||||
|
<string name="config_bt_slice_pending_intent_action" translatable="false"></string>
|
||||||
|
<!-- BT Slice EXTRA_INTENT. To support Settings 2 panel, BT slice can't use PendingIntent.send(). Therefore, here defines the Slice EXTRA_INTENT. -->
|
||||||
|
<string name="config_bt_slice_extra_intent" translatable="false"></string>
|
||||||
|
<!-- BT Slice EXTRA_PENDING_INTENT. To support Settings 2 panel, BT slice can't use PendingIntent.send(). Therefore, here defines the Slice EXTRA_PENDING_INTENT. -->
|
||||||
|
<string name="config_bt_slice_extra_pending_intent" translatable="false"></string>
|
||||||
|
|
||||||
<!-- Grayscale settings intent -->
|
<!-- Grayscale settings intent -->
|
||||||
<string name="config_grayscale_settings_intent" translatable="false"></string>
|
<string name="config_grayscale_settings_intent" translatable="false"></string>
|
||||||
|
|
||||||
|
@@ -58,11 +58,9 @@
|
|||||||
settings:controller="com.android.settings.slices.SlicePreferenceController"
|
settings:controller="com.android.settings.slices.SlicePreferenceController"
|
||||||
settings:allowDividerAbove="true"/>
|
settings:allowDividerAbove="true"/>
|
||||||
|
|
||||||
<com.android.settings.slices.SlicePreference
|
<PreferenceCategory
|
||||||
android:key="bt_device_slice"
|
android:key="bt_device_slice_category"
|
||||||
settings:controller="com.android.settings.slices.BlockingSlicePrefController"
|
settings:controller="com.android.settings.bluetooth.BlockingPrefWithSliceController"/>
|
||||||
settings:allowDividerBelow="true"
|
|
||||||
settings:allowDividerAbove="true"/>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="device_companion_apps"/>
|
android:key="device_companion_apps"/>
|
||||||
|
@@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 android.app.slice.Slice.HINT_PERMISSION_REQUEST;
|
||||||
|
import static android.app.slice.Slice.HINT_TITLE;
|
||||||
|
import static android.app.slice.SliceItem.FORMAT_ACTION;
|
||||||
|
import static android.app.slice.SliceItem.FORMAT_IMAGE;
|
||||||
|
import static android.app.slice.SliceItem.FORMAT_SLICE;
|
||||||
|
import static android.app.slice.SliceItem.FORMAT_TEXT;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.slice.Slice;
|
||||||
|
import androidx.slice.SliceItem;
|
||||||
|
import androidx.slice.builders.ListBuilder;
|
||||||
|
import androidx.slice.builders.SliceAction;
|
||||||
|
import androidx.slice.widget.SliceLiveData;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
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;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The blocking preference with slice controller will make whole page invisible for a certain time
|
||||||
|
* until {@link Slice} is fully loaded.
|
||||||
|
*/
|
||||||
|
public class BlockingPrefWithSliceController extends BasePreferenceController implements
|
||||||
|
LifecycleObserver, OnStart, OnStop, Observer<Slice>, BasePreferenceController.UiBlocker{
|
||||||
|
private static final String TAG = "BlockingPrefWithSliceController";
|
||||||
|
|
||||||
|
private static final String PREFIX_KEY = "slice_preference_item_";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
LiveData<Slice> mLiveData;
|
||||||
|
private Uri mUri;
|
||||||
|
@VisibleForTesting
|
||||||
|
PreferenceCategory mPreferenceCategory;
|
||||||
|
private List<Preference> mCurrentPreferencesList = new ArrayList<>();
|
||||||
|
@VisibleForTesting
|
||||||
|
String mSliceIntentAction = "";
|
||||||
|
@VisibleForTesting
|
||||||
|
String mSlicePendingIntentAction = "";
|
||||||
|
@VisibleForTesting
|
||||||
|
String mExtraIntent = "";
|
||||||
|
@VisibleForTesting
|
||||||
|
String mExtraPendingIntent = "";
|
||||||
|
|
||||||
|
public BlockingPrefWithSliceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
mPreferenceCategory = screen.findPreference(getPreferenceKey());
|
||||||
|
mSliceIntentAction = mContext.getResources().getString(
|
||||||
|
R.string.config_bt_slice_intent_action);
|
||||||
|
mSlicePendingIntentAction = mContext.getResources().getString(
|
||||||
|
R.string.config_bt_slice_pending_intent_action);
|
||||||
|
mExtraIntent = mContext.getResources().getString(R.string.config_bt_slice_extra_intent);
|
||||||
|
mExtraPendingIntent = mContext.getResources().getString(
|
||||||
|
R.string.config_bt_slice_extra_pending_intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return mUri != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSliceUri(Uri uri) {
|
||||||
|
mUri = uri;
|
||||||
|
mLiveData = SliceLiveData.fromUri(mContext, mUri, (int type, Throwable source) -> {
|
||||||
|
Log.w(TAG, "Slice may be null. uri = " + uri + ", error = " + type);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO(b/120803703): figure out why we need to remove observer first
|
||||||
|
mLiveData.removeObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
if (mLiveData != null) {
|
||||||
|
mLiveData.observeForever(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
if (mLiveData != null) {
|
||||||
|
mLiveData.removeObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(Slice slice) {
|
||||||
|
updatePreferenceFromSlice(slice);
|
||||||
|
if (mUiBlockListener != null) {
|
||||||
|
mUiBlockListener.onBlockerWorkFinished(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void updatePreferenceFromSlice(Slice slice) {
|
||||||
|
if (TextUtils.isEmpty(mSliceIntentAction)
|
||||||
|
|| TextUtils.isEmpty(mExtraIntent)
|
||||||
|
|| TextUtils.isEmpty(mSlicePendingIntentAction)
|
||||||
|
|| TextUtils.isEmpty(mExtraPendingIntent)) {
|
||||||
|
Log.d(TAG, "No configs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (slice == null || slice.hasHint(HINT_PERMISSION_REQUEST)) {
|
||||||
|
Log.d(TAG, "Current slice: " + slice);
|
||||||
|
removePreferenceListFromPreferenceCategory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updatePreferenceListAndPreferenceCategory(parseSliceToPreferenceList(slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Preference> parseSliceToPreferenceList(Slice slice) {
|
||||||
|
List<Preference> preferenceItemsList = new ArrayList<>();
|
||||||
|
List<SliceItem> items = slice.getItems();
|
||||||
|
int orderLevel = 0;
|
||||||
|
for (SliceItem sliceItem : items) {
|
||||||
|
// Parse the slice
|
||||||
|
if (sliceItem.getFormat().equals(FORMAT_SLICE)) {
|
||||||
|
Optional<CharSequence> title = extractTitleFromSlice(sliceItem.getSlice());
|
||||||
|
Optional<CharSequence> subtitle = extractSubtitleFromSlice(sliceItem.getSlice());
|
||||||
|
Optional<SliceAction> action = extractActionFromSlice(sliceItem.getSlice());
|
||||||
|
// Create preference
|
||||||
|
Optional<Preference> preferenceItem = createPreferenceItem(title, subtitle, action,
|
||||||
|
orderLevel);
|
||||||
|
if (preferenceItem.isPresent()) {
|
||||||
|
orderLevel++;
|
||||||
|
preferenceItemsList.add(preferenceItem.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return preferenceItemsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Preference> createPreferenceItem(Optional<CharSequence> title,
|
||||||
|
Optional<CharSequence> subtitle, Optional<SliceAction> sliceAction, int orderLevel) {
|
||||||
|
Log.d(TAG, "Title: " + title.orElse("no title")
|
||||||
|
+ ", Subtitle: " + subtitle.orElse("no Subtitle")
|
||||||
|
+ ", Action: " + sliceAction.orElse(null));
|
||||||
|
if (!title.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
String key = PREFIX_KEY + title.get();
|
||||||
|
Preference preference = mPreferenceCategory.findPreference(key);
|
||||||
|
if (preference == null) {
|
||||||
|
preference = new Preference(mContext);
|
||||||
|
preference.setKey(key);
|
||||||
|
mPreferenceCategory.addPreference(preference);
|
||||||
|
}
|
||||||
|
preference.setTitle(title.get());
|
||||||
|
preference.setOrder(orderLevel);
|
||||||
|
if (subtitle.isPresent()) {
|
||||||
|
preference.setSummary(subtitle.get());
|
||||||
|
}
|
||||||
|
if (sliceAction.isPresent()) {
|
||||||
|
// To support the settings' 2 panel feature, here can't use the slice's
|
||||||
|
// PendingIntent.send(). Since the PendingIntent.send() always take NEW_TASK flag.
|
||||||
|
// Therefore, transfer the slice's PendingIntent to Intent and start it
|
||||||
|
// without NEW_TASK.
|
||||||
|
preference.setIcon(sliceAction.get().getIcon().loadDrawable(mContext));
|
||||||
|
Intent intentFromSliceAction = sliceAction.get().getAction().getIntent();
|
||||||
|
Intent expectedActivityIntent = null;
|
||||||
|
Log.d(TAG, "SliceAction: intent's Action:" + intentFromSliceAction.getAction());
|
||||||
|
if (intentFromSliceAction.getAction().equals(mSliceIntentAction)) {
|
||||||
|
expectedActivityIntent = intentFromSliceAction
|
||||||
|
.getParcelableExtra(mExtraIntent, Intent.class);
|
||||||
|
} else if (intentFromSliceAction.getAction().equals(
|
||||||
|
mSlicePendingIntentAction)) {
|
||||||
|
PendingIntent pendingIntent = intentFromSliceAction
|
||||||
|
.getParcelableExtra(mExtraPendingIntent, PendingIntent.class);
|
||||||
|
expectedActivityIntent =
|
||||||
|
pendingIntent != null ? pendingIntent.getIntent() : null;
|
||||||
|
} else {
|
||||||
|
expectedActivityIntent = intentFromSliceAction;
|
||||||
|
}
|
||||||
|
if (expectedActivityIntent != null) {
|
||||||
|
Log.d(TAG, "setIntent: ActivityIntent" + expectedActivityIntent);
|
||||||
|
// Since UI needs to support the Settings' 2 panel feature, the intent can't use the
|
||||||
|
// FLAG_ACTIVITY_NEW_TASK. The above intent may have the FLAG_ACTIVITY_NEW_TASK
|
||||||
|
// flag, so removes it before startActivity(preference.setIntent).
|
||||||
|
expectedActivityIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
preference.setIntent(expectedActivityIntent);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "setIntent: Intent is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removePreferenceListFromPreferenceCategory() {
|
||||||
|
mCurrentPreferencesList.stream()
|
||||||
|
.forEach(p -> mPreferenceCategory.removePreference(p));
|
||||||
|
mCurrentPreferencesList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePreferenceListAndPreferenceCategory(List<Preference> newPreferenceList) {
|
||||||
|
List<Preference> removedItemList = new ArrayList<>(mCurrentPreferencesList);
|
||||||
|
for (Preference item : mCurrentPreferencesList) {
|
||||||
|
if (newPreferenceList.stream().anyMatch(p -> item.compareTo(p) == 0)) {
|
||||||
|
removedItemList.remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedItemList.stream()
|
||||||
|
.forEach(p -> mPreferenceCategory.removePreference(p));
|
||||||
|
mCurrentPreferencesList = newPreferenceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<CharSequence> extractTitleFromSlice(Slice slice) {
|
||||||
|
return extractTextFromSlice(slice, HINT_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<CharSequence> extractSubtitleFromSlice(Slice slice) {
|
||||||
|
// For subtitle items, there isn't a hint available.
|
||||||
|
return extractTextFromSlice(slice, /* hint= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<CharSequence> extractTextFromSlice(Slice slice, @Nullable String hint) {
|
||||||
|
for (SliceItem item : slice.getItems()) {
|
||||||
|
if (item.getFormat().equals(FORMAT_TEXT)
|
||||||
|
&& ((TextUtils.isEmpty(hint) && item.getHints().isEmpty())
|
||||||
|
|| (!TextUtils.isEmpty(hint) && item.hasHint(hint)))) {
|
||||||
|
return Optional.ofNullable(item.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<SliceAction> extractActionFromSlice(Slice slice) {
|
||||||
|
for (SliceItem item : slice.getItems()) {
|
||||||
|
if (item.getFormat().equals(FORMAT_SLICE)) {
|
||||||
|
if (item.hasHint(HINT_TITLE)) {
|
||||||
|
Optional<SliceAction> result = extractActionFromSlice(item.getSlice());
|
||||||
|
if (result.isPresent()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getFormat().equals(FORMAT_ACTION)) {
|
||||||
|
Optional<IconCompat> icon = extractIconFromSlice(item.getSlice());
|
||||||
|
Optional<CharSequence> title = extractTitleFromSlice(item.getSlice());
|
||||||
|
if (icon.isPresent()) {
|
||||||
|
return Optional.of(
|
||||||
|
SliceAction.create(
|
||||||
|
item.getAction(),
|
||||||
|
icon.get(),
|
||||||
|
ListBuilder.ICON_IMAGE,
|
||||||
|
title.orElse(/* other= */ "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<IconCompat> extractIconFromSlice(Slice slice) {
|
||||||
|
for (SliceItem item : slice.getItems()) {
|
||||||
|
if (item.getFormat().equals(FORMAT_IMAGE)) {
|
||||||
|
return Optional.of(item.getIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
@@ -42,7 +42,6 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.core.SettingsUIDeviceConfig;
|
import com.android.settings.core.SettingsUIDeviceConfig;
|
||||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.slices.BlockingSlicePrefController;
|
|
||||||
import com.android.settings.slices.SlicePreferenceController;
|
import com.android.settings.slices.SlicePreferenceController;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
@@ -133,7 +132,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
|||||||
final boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
final boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||||
SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true);
|
SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true);
|
||||||
|
|
||||||
use(BlockingSlicePrefController.class).setSliceUri(sliceEnabled
|
use(BlockingPrefWithSliceController.class).setSliceUri(sliceEnabled
|
||||||
? featureProvider.getBluetoothDeviceSettingsUri(mCachedDevice.getDevice())
|
? featureProvider.getBluetoothDeviceSettingsUri(mCachedDevice.getDevice())
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.slice.Slice;
|
||||||
|
import androidx.slice.SliceViewManager;
|
||||||
|
import androidx.slice.builders.ListBuilder;
|
||||||
|
import androidx.slice.builders.ListBuilder.RowBuilder;
|
||||||
|
import androidx.slice.builders.SliceAction;
|
||||||
|
import androidx.test.annotation.UiThreadTest;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.settings.bluetooth.BlockingPrefWithSliceController;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
public class BlockingPrefWithSliceControllerTest {
|
||||||
|
private static final String KEY = "bt_device_slice_category";
|
||||||
|
private static final String TEST_URI_AUTHORITY = "com.android.authority.test";
|
||||||
|
private static final String TEST_EXTRA_INTENT = "EXTRA_INTENT";
|
||||||
|
private static final String TEST_EXTRA_PENDING_INTENT = "EXTRA_PENDING_INTENT";
|
||||||
|
private static final String TEST_INTENT_ACTION = "test";
|
||||||
|
private static final String TEST_PENDING_INTENT_ACTION = "test";
|
||||||
|
private static final String TEST_SLICE_TITLE = "Test Title";
|
||||||
|
private static final String TEST_SLICE_SUBTITLE = "Test Subtitle";
|
||||||
|
private static final String FAKE_ACTION = "fake_action";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LiveData<Slice> mLiveData;
|
||||||
|
@Mock
|
||||||
|
private PreferenceCategory mPreferenceCategory;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private BlockingPrefWithSliceController mController;
|
||||||
|
private Uri mUri;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
mController = spy(new BlockingPrefWithSliceController(mContext, KEY));
|
||||||
|
mController.mLiveData = mLiveData;
|
||||||
|
mController.mExtraIntent = TEST_EXTRA_INTENT;
|
||||||
|
mController.mExtraPendingIntent = TEST_EXTRA_PENDING_INTENT;
|
||||||
|
mController.mSliceIntentAction = TEST_INTENT_ACTION;
|
||||||
|
mController.mSlicePendingIntentAction = TEST_PENDING_INTENT_ACTION;
|
||||||
|
mController.mPreferenceCategory = mPreferenceCategory;
|
||||||
|
mUri = Uri.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isAvailable_uriNull_returnFalse() {
|
||||||
|
assertThat(mController.isAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@UiThreadTest
|
||||||
|
public void isAvailable_uriNotNull_returnTrue() {
|
||||||
|
mController.setSliceUri(mUri);
|
||||||
|
|
||||||
|
assertThat(mController.isAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_registerObserver() {
|
||||||
|
mController.onStart();
|
||||||
|
|
||||||
|
verify(mLiveData).observeForever(mController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_unregisterObserver() {
|
||||||
|
mController.onStop();
|
||||||
|
|
||||||
|
verify(mLiveData).removeObserver(mController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChanged_nullSlice_updateSlice() {
|
||||||
|
mController.onChanged(null);
|
||||||
|
|
||||||
|
verify(mController).updatePreferenceFromSlice(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChanged_testSlice_updateSlice() {
|
||||||
|
mController.onChanged(buildTestSlice());
|
||||||
|
|
||||||
|
verify(mController.mPreferenceCategory).addPreference(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Slice buildTestSlice() {
|
||||||
|
Uri uri =
|
||||||
|
new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(TEST_URI_AUTHORITY)
|
||||||
|
.build();
|
||||||
|
SliceViewManager.getInstance(mContext).pinSlice(uri);
|
||||||
|
ListBuilder listBuilder = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
|
||||||
|
IconCompat icon = mock(IconCompat.class);
|
||||||
|
listBuilder.addRow(
|
||||||
|
new RowBuilder()
|
||||||
|
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
|
||||||
|
.setTitle(TEST_SLICE_TITLE)
|
||||||
|
.setSubtitle(TEST_SLICE_SUBTITLE)
|
||||||
|
.setPrimaryAction(
|
||||||
|
SliceAction.create(
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
mContext,
|
||||||
|
/*requestCode= */ 0,
|
||||||
|
new Intent(FAKE_ACTION),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
| PendingIntent.FLAG_IMMUTABLE),
|
||||||
|
icon,
|
||||||
|
ListBuilder.ICON_IMAGE,
|
||||||
|
"")));
|
||||||
|
return listBuilder.build();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user