import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
+import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
public class CreateShortcut extends LauncherActivity {
+ private static final String TAG = "CreateShortcut";
@VisibleForTesting
static final String SHORTCUT_ID_PREFIX = "component-shortcut-";
ShortcutManager sm = getSystemService(ShortcutManager.class);
ActivityInfo activityInfo = resolveInfo.activityInfo;
- Icon maskableIcon = activityInfo.icon != 0 ? Icon.createWithAdaptiveBitmap(
- createIcon(activityInfo.icon,
+ Icon maskableIcon = activityInfo.icon != 0 && activityInfo.applicationInfo != null
+ ? Icon.createWithAdaptiveBitmap(
+ createIcon(activityInfo.applicationInfo, activityInfo.icon,
R.layout.shortcut_badge_maskable,
- getResources().getDimensionPixelSize(R.dimen.shortcut_size_maskable))) :
- Icon.createWithResource(this, R.drawable.ic_launcher_settings);
+ getResources().getDimensionPixelSize(R.dimen.shortcut_size_maskable)))
+ : Icon.createWithResource(this, R.drawable.ic_launcher_settings);
String shortcutId = SHORTCUT_ID_PREFIX +
shortcutIntent.getComponent().flattenToShortString();
ShortcutInfo info = new ShortcutInfo.Builder(this, shortcutId)
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
if (activityInfo.icon != 0) {
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(activityInfo.icon,
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(
+ activityInfo.applicationInfo,
+ activityInfo.icon,
R.layout.shortcut_badge,
getResources().getDimensionPixelSize(R.dimen.shortcut_size)));
}
info.activityInfo.name);
}
- private Bitmap createIcon(int resource, int layoutRes, int size) {
- Context context = new ContextThemeWrapper(this, android.R.style.Theme_Material);
- View view = LayoutInflater.from(context).inflate(layoutRes, null);
- Drawable iconDrawable = getDrawable(resource);
- if (iconDrawable instanceof LayerDrawable) {
- iconDrawable = ((LayerDrawable) iconDrawable).getDrawable(1);
- }
- ((ImageView) view.findViewById(android.R.id.icon)).setImageDrawable(iconDrawable);
-
- int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ private Bitmap createIcon(ApplicationInfo app, int resource, int layoutRes, int size) {
+ final Context context = new ContextThemeWrapper(this, android.R.style.Theme_Material);
+ final View view = LayoutInflater.from(context).inflate(layoutRes, null);
+ final int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
view.measure(spec, spec);
- Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
+ final Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
+ final Canvas canvas = new Canvas(bitmap);
+
+ Drawable iconDrawable = null;
+ try {
+ iconDrawable =
+ getPackageManager().getResourcesForApplication(app).getDrawable(resource);
+ if (iconDrawable instanceof LayerDrawable) {
+ iconDrawable = ((LayerDrawable) iconDrawable).getDrawable(1);
+ }
+ ((ImageView) view.findViewById(android.R.id.icon)).setImageDrawable(iconDrawable);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Cannot load icon from app " + app + ", returning a default icon");
+ Icon icon = Icon.createWithResource(this, R.drawable.ic_launcher_settings);
+ ((ImageView) view.findViewById(android.R.id.icon)).setImageIcon(icon);
+ }
+
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.draw(canvas);
return bitmap;
* Perform query on package manager for list items. The default
* implementation queries for activities.
*/
+ @Override
protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
List<ResolveInfo> activities = getPackageManager().queryIntentActivities(queryIntent,
PackageManager.GET_META_DATA);
final ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- if (activities == null) return null;
+ if (activities == null) {
+ return null;
+ }
for (int i = activities.size() - 1; i >= 0; i--) {
ResolveInfo info = activities.get(i);
if (info.activityInfo.name.endsWith(TetherSettingsActivity.class.getSimpleName())) {
activities.remove(i);
}
}
+ if (!info.activityInfo.applicationInfo.isSystemApp()) {
+ Log.d(TAG, "Skipping non-system app: " + info.activityInfo);
+ activities.remove(i);
+ }
}
return activities;
}
--- /dev/null
+/*
+ * 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.shortcut;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+
+import com.android.settings.Settings;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowConnectivityManager;
+
+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 org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowPackageManager;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link CreateShortcutTest}
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = ShadowConnectivityManager.class)
+public class CreateShortcutTest {
+
+ private static final String SHORTCUT_ID_PREFIX = CreateShortcut.SHORTCUT_ID_PREFIX;
+
+ private Context mContext;
+ private ShadowConnectivityManager mShadowConnectivityManager;
+ private ShadowPackageManager mPackageManager;
+
+ @Mock
+ private ShortcutManager mShortcutManager;
+ @Captor
+ private ArgumentCaptor<List<ShortcutInfo>> mListCaptor;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mPackageManager = Shadow.extract(mContext.getPackageManager());
+ mShadowConnectivityManager = ShadowConnectivityManager.getShadow();
+ mShadowConnectivityManager.setTetheringSupported(true);
+ }
+
+ @Test
+ public void createResultIntent() {
+ CreateShortcut orgActivity = Robolectric.setupActivity(CreateShortcut.class);
+ 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"));
+
+ final Intent intent = CreateShortcut.getBaseIntent()
+ .setClass(activity, Settings.ManageApplicationsActivity.class);
+ final ResolveInfo ri = activity.getPackageManager().resolveActivity(intent, 0);
+ final Intent result = activity.createResultIntent(intent, ri, "dummy");
+
+ assertThat(result.getStringExtra("d1")).isEqualTo("d2");
+ assertThat((Object) result.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT)).isNotNull();
+
+ ArgumentCaptor<ShortcutInfo> infoCaptor = ArgumentCaptor.forClass(ShortcutInfo.class);
+ verify(mShortcutManager, times(1))
+ .createShortcutResultIntent(infoCaptor.capture());
+ assertThat(infoCaptor.getValue().getId())
+ .isEqualTo(SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString());
+ }
+
+ @Test
+ public void shortcutsUpdateTask() {
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));
+ final Intent shortcut1 = CreateShortcut.getBaseIntent().setComponent(
+ new ComponentName(mContext, Settings.ManageApplicationsActivity.class));
+ final ResolveInfo ri1 = mock(ResolveInfo.class);
+ final Intent shortcut2 = CreateShortcut.getBaseIntent().setComponent(
+ new ComponentName(mContext, Settings.SoundSettingsActivity.class));
+ final ResolveInfo ri2 = mock(ResolveInfo.class);
+ when(ri1.loadLabel(any(PackageManager.class))).thenReturn("label1");
+ when(ri2.loadLabel(any(PackageManager.class))).thenReturn("label2");
+ mPackageManager.addResolveInfoForIntent(shortcut1, ri1);
+ mPackageManager.addResolveInfoForIntent(shortcut2, ri2);
+
+ final List<ShortcutInfo> 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());
+
+ final List<ShortcutInfo> updates = mListCaptor.getValue();
+
+ assertThat(updates).hasSize(2);
+ assertThat(pinnedShortcuts.get(2).getId()).isEqualTo(updates.get(0).getId());
+ assertThat(pinnedShortcuts.get(4).getId()).isEqualTo(updates.get(1).getId());
+ }
+
+ @Test
+ public void queryActivities_shouldOnlyIncludeSystemApp() {
+ final ResolveInfo ri1 = new ResolveInfo();
+ ri1.activityInfo = new ActivityInfo();
+ ri1.activityInfo.name = "activity1";
+ ri1.activityInfo.applicationInfo = new ApplicationInfo();
+ ri1.activityInfo.applicationInfo.flags = 0;
+ final ResolveInfo ri2 = new ResolveInfo();
+ ri2.activityInfo = new ActivityInfo();
+ ri2.activityInfo.name = "activity2";
+ ri2.activityInfo.applicationInfo = new ApplicationInfo();
+ ri2.activityInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ mPackageManager.addResolveInfoForIntent(CreateShortcut.getBaseIntent(),
+ Arrays.asList(ri1, ri2));
+
+ TestClass orgActivity = Robolectric.setupActivity(TestClass.class);
+ TestClass activity = spy(orgActivity);
+
+ List<ResolveInfo> info = activity.onQueryPackageManager(CreateShortcut.getBaseIntent());
+ assertThat(info).hasSize(1);
+ assertThat(info.get(0)).isEqualTo(ri2);
+ }
+
+ 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();
+ }
+
+ private static class TestClass extends CreateShortcut {
+ }
+}
import android.net.ConnectivityManager;
import android.util.SparseBooleanArray;
+import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
@Implements(value = ConnectivityManager.class, inheritImplementationMethods = true)
public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
public boolean isTetheringSupported() {
return mTetheringSupported;
}
+
+ public static ShadowConnectivityManager getShadow() {
+ return Shadow.extract(
+ RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
+ }
}
+++ /dev/null
-/*
- * Copyright (C) 2016 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.shortcut;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-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.espresso.matcher.ViewMatchers;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.settings.R;
-import com.android.settings.Settings;
-
-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}
- */
-@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<List<ShortcutInfo>> mListCaptor;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mContext = mInstrumentation.getTargetContext();
- }
-
- @Test
- public void test_layoutDoesNotHaveCancelButton() {
- mInstrumentation.startActivitySync(new Intent(Intent.ACTION_CREATE_SHORTCUT)
- .setClassName(mContext, CreateShortcut.class.getName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- onView(ViewMatchers.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())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- 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<ShortcutInfo> 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<ShortcutInfo> 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<ShortcutInfo> 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();
- }
-}