Merge "Architecture review of Copyable Slice"
diff --git a/res/drawable/ic_content_copy_grey600_24dp.xml b/res/drawable/ic_content_copy_grey600_24dp.xml
new file mode 100644
index 0000000..827c66e
--- /dev/null
+++ b/res/drawable/ic_content_copy_grey600_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"
+ android:fillColor="#757575"/>
+</vector>
diff --git a/src/com/android/settings/slices/CopyableSlice.java b/src/com/android/settings/slices/CopyableSlice.java
new file mode 100644
index 0000000..31fc151
--- /dev/null
+++ b/src/com/android/settings/slices/CopyableSlice.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.slices;
+
+/**
+ * Provide the copy ability for preference controller to copy the data to the clipboard.
+ */
+public interface CopyableSlice {
+ /**
+ * Copy the key slice information to the clipboard.
+ * It is highly recommended to show the toast to notify users when implemented this function.
+ */
+ void copy();
+}
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 33576f5..fa669bb 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -107,6 +107,12 @@
"com.android.settings.slice.action.SLIDER_CHANGED";
/**
+ * Action passed for copy data for the Copyable Slices.
+ */
+ public static final String ACTION_COPY =
+ "com.android.settings.slice.action.COPY";
+
+ /**
* Intent Extra passed for the key identifying the Setting Slice.
*/
public static final String EXTRA_SLICE_KEY = "com.android.settings.slice.extra.key";
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index e9e9d2c..4b86f33 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -24,6 +24,7 @@
import static com.android.settings.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED;
import static com.android.settings.slices.SettingsSliceProvider.ACTION_SLIDER_CHANGED;
import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED;
+import static com.android.settings.slices.SettingsSliceProvider.ACTION_COPY;
import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED;
import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED;
@@ -115,6 +116,9 @@
case ACTION_FLASHLIGHT_SLICE_CHANGED:
FlashlightSliceBuilder.handleUriChange(context, intent);
break;
+ case ACTION_COPY:
+ handleCopyAction(context, key, isPlatformSlice);
+ break;
}
}
@@ -184,6 +188,29 @@
updateUri(context, key, isPlatformSlice);
}
+ private void handleCopyAction(Context context, String key, boolean isPlatformSlice) {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("No key passed to Intent for controller");
+ }
+
+ final BasePreferenceController controller = getPreferenceController(context, key);
+
+ if (!(controller instanceof CopyableSlice)) {
+ throw new IllegalArgumentException(
+ "Copyable action passed for a non-copyable key:" + key);
+ }
+
+ if (!controller.isAvailable()) {
+ Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
+ if (!controller.hasAsyncUpdate()) {
+ updateUri(context, key, isPlatformSlice);
+ }
+ return;
+ }
+
+ ((CopyableSlice) controller).copy();
+ }
+
/**
* Log Slice value update events into MetricsFeatureProvider. The logging schema generally
* follows the pattern in SharedPreferenceLogger.
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index 2432cc6..f6cc57f 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -93,6 +93,10 @@
return buildUnavailableSlice(context, sliceData);
}
+ if (controller instanceof CopyableSlice) {
+ return buildCopyableSlice(context, sliceData, controller);
+ }
+
switch (sliceData.getSliceType()) {
case SliceData.SliceType.INTENT:
return buildIntentSlice(context, sliceData, controller);
@@ -324,6 +328,28 @@
.build();
}
+ private static Slice buildCopyableSlice(Context context, SliceData sliceData,
+ BasePreferenceController controller) {
+ final SliceAction copyableAction = getCopyableAction(context, sliceData);
+ final PendingIntent contentIntent = getContentPendingIntent(context, sliceData);
+ final IconCompat icon = getSafeIcon(context, sliceData);
+ final SliceAction primaryAction = new SliceAction(contentIntent, icon,
+ sliceData.getTitle());
+ final CharSequence subtitleText = getSubtitleText(context, controller, sliceData);
+ @ColorInt final int color = Utils.getColorAccentDefaultColor(context);
+ final Set<String> keywords = buildSliceKeywords(sliceData);
+
+ return new ListBuilder(context, sliceData.getUri(), ListBuilder.INFINITY)
+ .setAccentColor(color)
+ .addRow(new RowBuilder()
+ .setTitle(sliceData.getTitle())
+ .setSubtitle(subtitleText)
+ .setPrimaryAction(primaryAction)
+ .addEndItem(copyableAction))
+ .setKeywords(keywords)
+ .build();
+ }
+
private static BasePreferenceController getPreferenceController(Context context,
String controllerClassName, String controllerKey) {
try {
@@ -346,6 +372,14 @@
return getActionIntent(context, SettingsSliceProvider.ACTION_SLIDER_CHANGED, sliceData);
}
+ private static SliceAction getCopyableAction(Context context, SliceData sliceData) {
+ final PendingIntent intent = getActionIntent(context,
+ SettingsSliceProvider.ACTION_COPY, sliceData);
+ final IconCompat icon = IconCompat.createWithResource(context,
+ R.drawable.ic_content_copy_grey600_24dp);
+ return new SliceAction(intent, icon, sliceData.getTitle());
+ }
+
private static boolean isValidSummary(Context context, CharSequence summary) {
if (summary == null || TextUtils.isEmpty(summary.toString().trim())) {
return false;
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index 1055667..4371278 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -41,6 +41,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.FakeCopyableController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.FakeSliderController;
import com.android.settings.testutils.FakeToggleController;
@@ -67,6 +68,7 @@
private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private final Class TOGGLE_CONTROLLER = FakeToggleController.class;
private final Class SLIDER_CONTROLLER = FakeSliderController.class;
+ private final Class COPYABLE_CONTROLLER = FakeCopyableController.class;
private final Class CONTEXT_CONTROLLER = FakeContextOnlyPreferenceController.class;
private final boolean IS_DYNAMIC_SUMMARY_ALLOWED = false;
@@ -116,7 +118,6 @@
public void buildSliderSlice_returnsMatchingSlice() {
final SliceData data = getDummyData(SLIDER_CONTROLLER, SliceData.SliceType.SLIDER);
-
final Slice slice = SliceBuilderUtils.buildSlice(mContext, data);
verify(mFeatureFactory.metricsFeatureProvider)
.action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED),
@@ -131,6 +132,23 @@
}
@Test
+ public void buildCopyableSlice_returnsMatchingSlice() {
+ final SliceData dummyData = getDummyData(COPYABLE_CONTROLLER, -1);
+
+ final Slice slice = SliceBuilderUtils.buildSlice(mContext, dummyData);
+ verify(mFeatureFactory.metricsFeatureProvider)
+ .action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED),
+ mLoggingArgumentCatpor.capture());
+ final Pair<Integer, Object> capturedLoggingPair = mLoggingArgumentCatpor.getValue();
+
+ assertThat(capturedLoggingPair.first)
+ .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME);
+ assertThat(capturedLoggingPair.second)
+ .isEqualTo(dummyData.getKey());
+ SliceTester.testSettingsCopyableSlice(mContext, slice, dummyData);
+ }
+
+ @Test
public void testUriBuilder_oemAuthority_intentPath_returnsValidSliceUri() {
final Uri expectedUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeCopyableController.java b/tests/robotests/src/com/android/settings/testutils/FakeCopyableController.java
new file mode 100644
index 0000000..a02377c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/FakeCopyableController.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.slices.CopyableSlice;
+
+public class FakeCopyableController extends BasePreferenceController implements
+ CopyableSlice {
+
+ public FakeCopyableController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return true;
+ }
+
+ @Override
+ public void copy() {
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
index 892f948..be4199d 100644
--- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java
+++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
@@ -169,6 +169,43 @@
}
/**
+ * Test the copyable slice, including:
+ * - No intent
+ * - Correct title
+ * - Correct intent
+ * - Correct keywords
+ * - TTL
+ * - Color
+ */
+ public static void testSettingsCopyableSlice(Context context, Slice slice,
+ SliceData sliceData) {
+ final SliceMetadata metadata = SliceMetadata.from(context, slice);
+
+ final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
+ final int color = colorItem.getInt();
+ assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
+
+ final SliceAction primaryAction = metadata.getPrimaryAction();
+
+ final IconCompat expectedIcon = IconCompat.createWithResource(context,
+ sliceData.getIconResource());
+ assertThat(expectedIcon.toString()).isEqualTo(primaryAction.getIcon().toString());
+
+ final long sliceTTL = metadata.getExpiry();
+ assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
+
+ // Check primary intent
+ final PendingIntent primaryPendingIntent = primaryAction.getAction();
+ assertThat(primaryPendingIntent).isEqualTo(
+ SliceBuilderUtils.getContentPendingIntent(context, sliceData));
+
+ final List<SliceItem> sliceItems = slice.getItems();
+ assertTitle(sliceItems, sliceData.getTitle());
+
+ assertKeywords(metadata, sliceData);
+ }
+
+ /**
* Test the contents of an unavailable slice, including:
* - No toggles
* - Correct title
@@ -229,4 +266,4 @@
expectedKeywords.add(data.getScreenTitle().toString());
assertThat(keywords).containsExactlyElementsIn(expectedKeywords);
}
-}
\ No newline at end of file
+}