Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61a6d527 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Misc tweaks to create shortcut handler"

parents b8643876 807dfbb0
Loading
Loading
Loading
Loading
+40 −18
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ 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;
@@ -33,6 +34,7 @@ import android.graphics.drawable.Icon;
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;
@@ -52,6 +54,7 @@ import androidx.annotation.VisibleForTesting;

public class CreateShortcut extends LauncherActivity {

    private static final String TAG = "CreateShortcut";
    @VisibleForTesting
    static final String SHORTCUT_ID_PREFIX = "component-shortcut-";

@@ -76,11 +79,12 @@ public class CreateShortcut extends LauncherActivity {
        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)
@@ -98,7 +102,9 @@ public class CreateShortcut extends LauncherActivity {
        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)));
        }
@@ -114,20 +120,29 @@ public class CreateShortcut extends LauncherActivity {
                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);
    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);
        final Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
                Config.ARGB_8888);
        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);
        }

        int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
        view.measure(spec, spec);
        Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
                Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.draw(canvas);
        return bitmap;
@@ -147,12 +162,15 @@ public class CreateShortcut extends LauncherActivity {
     * 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())) {
@@ -160,6 +178,10 @@ public class CreateShortcut extends LauncherActivity {
                    activities.remove(i);
                }
            }
            if (!info.activityInfo.applicationInfo.isSystemApp()) {
                Log.d(TAG, "Skipping non-system app: " + info.activityInfo);
                activities.remove(i);
            }
        }
        return activities;
    }
+179 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 * 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.
@@ -16,33 +16,29 @@

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 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.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
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 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 com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;

import org.junit.Before;
import org.junit.Test;
@@ -51,6 +47,11 @@ 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;
@@ -58,80 +59,110 @@ import java.util.List;
/**
 * Tests for {@link CreateShortcutTest}
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowConnectivityManager.class)
public class CreateShortcutTest {

    private static final String SHORTCUT_ID_PREFIX = CreateShortcut.SHORTCUT_ID_PREFIX;

    private Instrumentation mInstrumentation;
    private Context mContext;
    private ShadowConnectivityManager mShadowConnectivityManager;
    private ShadowPackageManager mPackageManager;

    @Mock
    ShortcutManager mShortcutManager;
    private ShortcutManager mShortcutManager;
    @Captor
    ArgumentCaptor<List<ShortcutInfo>> mListCaptor;
    private 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());
        mContext = RuntimeEnvironment.application;
        mPackageManager = Shadow.extract(mContext.getPackageManager());
        mShadowConnectivityManager = ShadowConnectivityManager.getShadow();
        mShadowConnectivityManager.setTetheringSupported(true);
    }

    @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 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"));

        Intent intent = CreateShortcut.getBaseIntent()
        final 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));
        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());
        String expectedId = SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString();
        assertEquals(expectedId, infoCaptor.getValue().getId());
        assertThat(infoCaptor.getValue().getId())
                .isEqualTo(SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString());
    }

    @Test
    public void shortcutsUpdateTask() {
        mContext = spy(new ContextWrapper(mInstrumentation.getTargetContext()));
        mContext = spy(RuntimeEnvironment.application);
        doReturn(mShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));

        List<ShortcutInfo> pinnedShortcuts = Arrays.asList(
                makeShortcut("d1"), makeShortcut("d2"),
        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());

        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());
        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) {
@@ -142,4 +173,7 @@ public class CreateShortcutTest {
    private ShortcutInfo makeShortcut(String id) {
        return new ShortcutInfo.Builder(mContext, id).build();
    }

    private static class TestClass extends CreateShortcut {
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.settings.testutils.shadow;
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 {
@@ -45,4 +47,9 @@ public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowCon
    public boolean isTetheringSupported() {
        return mTetheringSupported;
    }

    public static ShadowConnectivityManager getShadow() {
        return Shadow.extract(
                RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
    }
}