From 9a6b6750966789a38ab3de4cffab319c4c5dbffe Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Jan 2017 12:06:48 -0800 Subject: [PATCH] Updating CreateShortcut activity to use the new ShortcutManager APIs for creating shortcuts Test: adb shell am instrument -e class com.android.settings.CreateShortcutTest \ -w com.android.settings.tests/android.support.test.runner.AndroidJUnitRunner Change-Id: I09fb73ae2a0687a3269f3714bdb2c5d92c99ea87 --- src/com/android/settings/CreateShortcut.java | 91 +++++++++++-- .../LocaleDragAndDropAdapter.java | 3 + .../android/settings/CreateShortcutTest.java | 127 ++++++++++++++++-- 3 files changed, 194 insertions(+), 27 deletions(-) diff --git a/src/com/android/settings/CreateShortcut.java b/src/com/android/settings/CreateShortcut.java index 7cd6748a6d9..726bf0a88ec 100644 --- a/src/com/android/settings/CreateShortcut.java +++ b/src/com/android/settings/CreateShortcut.java @@ -17,15 +17,21 @@ package com.android.settings; import android.app.LauncherActivity; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; +import android.graphics.drawable.Icon; import android.net.ConnectivityManager; +import android.os.AsyncTask; +import android.support.annotation.VisibleForTesting; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; @@ -35,34 +41,52 @@ import android.widget.ListView; import com.android.settings.Settings.TetherSettingsActivity; +import java.util.ArrayList; import java.util.List; public class CreateShortcut extends LauncherActivity { + @VisibleForTesting + static final String SHORTCUT_ID_PREFIX = "component-shortcut-"; + @Override protected Intent getTargetIntent() { - Intent targetIntent = new Intent(Intent.ACTION_MAIN, null); - targetIntent.addCategory("com.android.settings.SHORTCUT"); - targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return targetIntent; + return getBaseIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { - Intent shortcutIntent = intentForPosition(position); + ListItem item = itemForPosition(position); + setResult(RESULT_OK, createResultIntent(intentForPosition(position), + item.resolveInfo, item.label)); + finish(); + } + + protected Intent createResultIntent(Intent shortcutIntent, ResolveInfo resolveInfo, + CharSequence label) { shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - Intent intent = new Intent(); + + ActivityInfo activityInfo = resolveInfo.activityInfo; + Bitmap icon = activityInfo.icon != 0 ? createIcon(activityInfo.icon) : null; + + String shortcutId = SHORTCUT_ID_PREFIX + + shortcutIntent.getComponent().flattenToShortString(); + ShortcutInfo info = new ShortcutInfo.Builder(this, shortcutId) + .setShortLabel(label) + .setIntent(shortcutIntent) + .setIcon(icon != null ? Icon.createWithBitmap(icon) : + Icon.createWithResource(this, R.mipmap.ic_launcher_settings)) + .build(); + Intent intent = getSystemService(ShortcutManager.class).createShortcutResultIntent(info); + if (intent == null) { + intent = new Intent(); + } intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_settings)); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, itemForPosition(position).label); - ResolveInfo resolveInfo = itemForPosition(position).resolveInfo; - ActivityInfo activityInfo = resolveInfo.activityInfo; - if (activityInfo.icon != 0) { - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(activityInfo.icon)); - } - setResult(RESULT_OK, intent); - finish(); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label); + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon); + return intent; } private Bitmap createIcon(int resource) { @@ -110,4 +134,43 @@ public class CreateShortcut extends LauncherActivity { } return activities; } + + @VisibleForTesting + static Intent getBaseIntent() { + return new Intent(Intent.ACTION_MAIN).addCategory("com.android.settings.SHORTCUT"); + } + + public static class ShortcutsUpdateTask extends AsyncTask { + + private final Context mContext; + + public ShortcutsUpdateTask(Context context) { + mContext = context; + } + + @Override + public Void doInBackground(Void... params) { + ShortcutManager sm = mContext.getSystemService(ShortcutManager.class); + PackageManager pm = mContext.getPackageManager(); + + List updates = new ArrayList<>(); + for (ShortcutInfo info : sm.getPinnedShortcuts()) { + if (!info.getId().startsWith(SHORTCUT_ID_PREFIX)) { + continue; + } + ComponentName cn = ComponentName.unflattenFromString( + info.getId().substring(SHORTCUT_ID_PREFIX.length())); + ResolveInfo ri = pm.resolveActivity(getBaseIntent().setComponent(cn), 0); + if (ri == null) { + continue; + } + updates.add(new ShortcutInfo.Builder(mContext, info.getId()) + .setShortLabel(ri.loadLabel(pm)).build()); + } + if (!updates.isEmpty()) { + sm.updateShortcuts(updates); + } + return null; + } + } } diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index 86498af9ab6..0b40bbd7703 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -34,6 +34,7 @@ import android.widget.CompoundButton; import com.android.internal.app.LocalePicker; import com.android.internal.app.LocaleStore; +import com.android.settings.CreateShortcut; import com.android.settings.R; import java.text.NumberFormat; @@ -299,6 +300,8 @@ class LocaleDragAndDropAdapter LocalePicker.updateLocales(mLocalesToSetNext); mLocalesSetLast = mLocalesToSetNext; + new CreateShortcut.ShortcutsUpdateTask(mContext).execute(); + mLocalesToSetNext = null; mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault()); diff --git a/tests/app/src/com/android/settings/CreateShortcutTest.java b/tests/app/src/com/android/settings/CreateShortcutTest.java index 9481a64ed04..4ae9bd7997c 100644 --- a/tests/app/src/com/android/settings/CreateShortcutTest.java +++ b/tests/app/src/com/android/settings/CreateShortcutTest.java @@ -16,29 +16,130 @@ package com.android.settings; -import android.app.Instrumentation; -import android.content.Intent; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link CreateShortcutTest} + * + m SettingsTests && + adb install \ + -r -g ${ANDROID_PRODUCT_OUT}/data/app/SettingsTests/SettingsTests.apk && + adb shell am instrument -e class com.android.settings.CreateShortcutTest \ + -w com.android.settings.tests/android.support.test.runner.AndroidJUnitRunner + */ @RunWith(AndroidJUnit4.class) @SmallTest public class CreateShortcutTest { + private static final String SHORTCUT_ID_PREFIX = CreateShortcut.SHORTCUT_ID_PREFIX; + + private Instrumentation mInstrumentation; + private Context mContext; + + @Mock ShortcutManager mShortcutManager; + @Captor ArgumentCaptor> mListCaptor; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mContext = mInstrumentation.getTargetContext(); + } + @Test public void test_layoutDoesNotHaveCancelButton() { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - instrumentation.startActivitySync(new Intent(Intent.ACTION_CREATE_SHORTCUT) - .setClassName(instrumentation.getTargetContext(), - CreateShortcut.class.getName())); + mInstrumentation.startActivitySync(new Intent(Intent.ACTION_CREATE_SHORTCUT) + .setClassName(mContext, CreateShortcut.class.getName())); onView(withText(R.string.cancel)).check(doesNotExist()); } + + @Test + public void createResultIntent() { + CreateShortcut orgActivity = (CreateShortcut) mInstrumentation.startActivitySync( + new Intent(Intent.ACTION_CREATE_SHORTCUT) + .setClassName(mContext, CreateShortcut.class.getName())); + CreateShortcut activity = spy(orgActivity); + doReturn(mShortcutManager).when(activity).getSystemService(eq(Context.SHORTCUT_SERVICE)); + + when(mShortcutManager.createShortcutResultIntent(any(ShortcutInfo.class))) + .thenReturn(new Intent().putExtra("d1", "d2")); + + Intent intent = CreateShortcut.getBaseIntent() + .setClass(activity, Settings.ManageApplicationsActivity.class); + ResolveInfo ri = activity.getPackageManager().resolveActivity(intent, 0); + Intent result = activity.createResultIntent(intent, ri, "dummy"); + assertEquals("d2", result.getStringExtra("d1")); + assertNotNull(result.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT)); + + ArgumentCaptor infoCaptor = ArgumentCaptor.forClass(ShortcutInfo.class); + verify(mShortcutManager, times(1)) + .createShortcutResultIntent(infoCaptor.capture()); + String expectedId = SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString(); + assertEquals(expectedId, infoCaptor.getValue().getId()); + } + + @Test + public void shortcutsUpdateTask() { + mContext = spy(new ContextWrapper(mInstrumentation.getTargetContext())); + doReturn(mShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE)); + + List pinnedShortcuts = Arrays.asList( + makeShortcut("d1"), makeShortcut("d2"), + makeShortcut(Settings.ManageApplicationsActivity.class), + makeShortcut("d3"), + makeShortcut(Settings.SoundSettingsActivity.class)); + when(mShortcutManager.getPinnedShortcuts()).thenReturn(pinnedShortcuts); + new CreateShortcut.ShortcutsUpdateTask(mContext).doInBackground(); + + verify(mShortcutManager, times(1)).updateShortcuts(mListCaptor.capture()); + + List updates = mListCaptor.getValue(); + assertEquals(2, updates.size()); + assertEquals(pinnedShortcuts.get(2).getId(), updates.get(0).getId()); + assertEquals(pinnedShortcuts.get(4).getId(), updates.get(1).getId()); + } + + private ShortcutInfo makeShortcut(Class className) { + ComponentName cn = new ComponentName(mContext, className); + return makeShortcut(SHORTCUT_ID_PREFIX + cn.flattenToShortString()); + } + + private ShortcutInfo makeShortcut(String id) { + return new ShortcutInfo.Builder(mContext, id).build(); + } }