diff options
31 files changed, 393 insertions, 262 deletions
diff --git a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml index bf76f29de..c02fe2cdc 100644 --- a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml +++ b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml @@ -21,5 +21,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string> <string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string> - <string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string> + <string name="set_wallpaper" msgid="6475195450505435904">"套用桌布"</string> </resources> diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java index 1e449108d..c20ed129c 100644 --- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -26,57 +26,46 @@ import android.os.UserHandle; import com.android.launcher3.ItemInfo; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; /** * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc. */ public class DeepShortcutManager { - private static DeepShortcutManager sInstance; - private static final Object sInstanceLock = new Object(); + + private static final DeepShortcutManager sInstance = new DeepShortcutManager(); public static DeepShortcutManager getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new DeepShortcutManager(context.getApplicationContext()); - } - return sInstance; - } + return sInstance; } - private DeepShortcutManager(Context context) { - } + private final QueryResult mFailure = new QueryResult(); - public static boolean supportsShortcuts(ItemInfo info) { - return false; - } + private DeepShortcutManager() { } - public boolean wasLastCallSuccess() { + public static boolean supportsShortcuts(ItemInfo info) { return false; } - public void onShortcutsChanged(List<ShortcutInfo> shortcuts) { - } - /** * Queries for the shortcuts with the package name and provided ids. * * This method is intended to get the full details for shortcuts when they are added or updated, * because we only get "key" fields in onShortcutsChanged(). */ - public List<ShortcutInfo> queryForFullDetails(String packageName, + public QueryResult queryForFullDetails(String packageName, List<String> shortcutIds, UserHandle user) { - return Collections.emptyList(); + return mFailure; } /** * Gets all the manifest and dynamic shortcuts associated with the given package and user, * to be displayed in the shortcuts container on long press. */ - public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity, + public QueryResult queryForShortcutsContainer(ComponentName activity, UserHandle user) { - return Collections.emptyList(); + return mFailure; } /** @@ -106,20 +95,28 @@ public class DeepShortcutManager { * * If packageName is null, returns all pinned shortcuts regardless of package. */ - public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) { - return Collections.emptyList(); + public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) { + return mFailure; } - public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, + public QueryResult queryForPinnedShortcuts(String packageName, List<String> shortcutIds, UserHandle user) { - return Collections.emptyList(); + return mFailure; } - public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) { - return Collections.emptyList(); + public QueryResult queryForAllShortcuts(UserHandle user) { + return mFailure; } public boolean hasHostPermission() { return false; } + + + public static class QueryResult extends ArrayList<ShortcutInfo> { + + public boolean wasSuccess() { + return true; + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java index 8f1282ded..fe0d16017 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java @@ -170,10 +170,10 @@ public class PredictionAppTracker extends AppLaunchTracker { public void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container) { // TODO: Use the full shortcut info - AppTarget target = new AppTarget - .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user) - .setClassName(shortcutId) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("shortcut:" + shortcutId), packageName, user) + .setClassName(shortcutId) + .build(); sendLaunch(target, container); } @@ -181,10 +181,10 @@ public class PredictionAppTracker extends AppLaunchTracker { @UiThread public void onStartApp(ComponentName cn, UserHandle user, String container) { if (cn != null) { - AppTarget target = new AppTarget - .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); sendLaunch(target, container); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java index f08ae4a82..9bdc98bf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -136,6 +136,12 @@ public final class RecentsActivity extends BaseRecentsActivity { } @Override + public void returnToHomescreen() { + super.returnToHomescreen(); + // TODO(b/137318995) This should go home, but doing so removes freeform windows + } + + @Override public ActivityOptions getActivityLaunchOptions(final View v) { if (!(v instanceof TaskView)) { return null; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index 213c5d324..cfd14bb29 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -36,8 +36,6 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -268,12 +266,16 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut @Override protected ActivityOptions makeLaunchOptions(Activity activity) { - return ActivityOptionsCompat.makeFreeformOptions(); + ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions(); + // Arbitrary bounds only because freeform is in dev mode right now + Rect r = new Rect(50, 50, 200, 200); + activityOptions.setLaunchBounds(r); + return activityOptions; } @Override protected boolean onActivityStarted(BaseDraggingActivity activity) { - Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL); + activity.returnToHomescreen(); return true; } } diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index e41dba94c..71ce32b28 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -20,10 +20,12 @@ import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityManager; -import android.content.Context; import android.os.Build; import android.os.Process; import android.util.SparseBooleanArray; + +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.MainThreadExecutor; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -42,6 +44,7 @@ public class RecentTasksList extends TaskStackChangeListener { private final KeyguardManagerCompat mKeyguardManager; private final MainThreadExecutor mMainThreadExecutor; + private final ActivityManagerWrapper mActivityManagerWrapper; // The list change id, increments as the task list changes in the system private int mChangeId; @@ -52,11 +55,14 @@ public class RecentTasksList extends TaskStackChangeListener { ArrayList<Task> mTasks = new ArrayList<>(); - public RecentTasksList(Context context) { - mMainThreadExecutor = new MainThreadExecutor(); - mKeyguardManager = new KeyguardManagerCompat(context); + + public RecentTasksList(MainThreadExecutor mainThreadExecutor, + KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) { + mMainThreadExecutor = mainThreadExecutor; + mKeyguardManager = keyguardManager; mChangeId = 1; - ActivityManagerWrapper.getInstance().registerTaskStackListener(this); + mActivityManagerWrapper = activityManagerWrapper; + mActivityManagerWrapper.registerTaskStackListener(this); } /** @@ -136,12 +142,13 @@ public class RecentTasksList extends TaskStackChangeListener { /** * Loads and creates a list of all the recent tasks. */ - private ArrayList<Task> loadTasksInBackground(int numTasks, + @VisibleForTesting + ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) { int currentUserId = Process.myUserHandle().getIdentifier(); ArrayList<Task> allTasks = new ArrayList<>(); List<ActivityManager.RecentTaskInfo> rawTasks = - ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId); + mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId); // The raw tasks are given in most-recent to least-recent order, we need to reverse it Collections.reverse(rawTasks); diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index 9f1248458..650169cf3 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -30,11 +30,14 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; +import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.KeyguardManagerCompat; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -68,7 +71,8 @@ public class RecentsModel extends TaskStackChangeListener { HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache", Process.THREAD_PRIORITY_BACKGROUND); loaderThread.start(); - mTaskList = new RecentTasksList(context); + mTaskList = new RecentTasksList(new MainThreadExecutor(), + new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance()); mIconCache = new TaskIconCache(context, loaderThread.getLooper()); mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper()); ActivityManagerWrapper.getInstance().registerTaskStackListener(this); diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index d0956d1f6..7801775d4 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -150,10 +150,10 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { List<AppTarget> targets = new ArrayList<>(activities.length); for (LauncherActivityInfo info : activities) { ComponentName cn = info.getComponentName(); - AppTarget target = - new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) + .setClassName(cn.getClassName()) + .build(); targets.add(target); } mCallback.onTargetsAvailable(targets); diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java new file mode 100644 index 000000000..5fb27bcc6 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 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.quickstep; + +import android.app.ActivityManager; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.MainThreadExecutor; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.KeyguardManagerCompat; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static junit.framework.TestCase.assertNull; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SmallTest +public class RecentTasksListTest { + + private ActivityManagerWrapper mockActivityManagerWrapper; + + // Class under test + private RecentTasksList mRecentTasksList; + + @Before + public void setup() { + MainThreadExecutor mockMainThreadExecutor = mock(MainThreadExecutor.class); + KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class); + mockActivityManagerWrapper = mock(ActivityManagerWrapper.class); + mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat, + mockActivityManagerWrapper); + } + + @Test + public void onTaskRemoved_reloadsAllTasks() { + mRecentTasksList.onTaskRemoved(0); + verify(mockActivityManagerWrapper, times(1)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void onTaskStackChanged_doesNotFetchTasks() { + mRecentTasksList.onTaskStackChanged(); + verify(mockActivityManagerWrapper, times(0)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void loadTasksInBackground_onlyKeys_noValidTaskDescription() { + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true); + + assertEquals(1, taskList.size()); + assertNull(taskList.get(0).taskDescription.getLabel()); + } + + @Test + public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() { + String taskDescription = "Wheeee!"; + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false); + + assertEquals(1, taskList.size()); + assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel()); + } +} diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index b796140e5..3b0432ee4 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -88,7 +88,7 @@ <string name="notification_dots_title" msgid="9062440428204120317">"नई सूचनाएं बताने वाला गोल निशान"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"चालू"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"चालू"</string> - <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचना के एक्सेस की ज़रूरत है"</string> + <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचना के ऐक्सेस की ज़रूरत है"</string> <string name="msg_missing_notification_access" msgid="281113995110910548">"सूचना बिंदु दिखाने के लिए, <xliff:g id="NAME">%1$s</xliff:g> के ऐप्लिकेशन सूचना चालू करें"</string> <string name="title_change_settings" msgid="1376365968844349552">"सेटिंग बदलें"</string> <string name="notification_dots_service_title" msgid="4284221181793592871">"नई सूचनाएं बताने वाला गोल निशान दिखाएं"</string> diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java new file mode 100644 index 000000000..c08e198a8 --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 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.launcher3.util; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** + * Robolectric unit tests for {@link IntArray} + */ +@RunWith(RobolectricTestRunner.class) +public class IntArrayTest { + + @Test + public void concatAndParseString() { + int[] array = new int[] {0, 2, 3, 9}; + String concat = IntArray.wrap(array).toConcatString(); + + int[] parsed = IntArray.fromConcatString(concat).toArray(); + assertThat(array).isEqualTo(parsed); + } +} diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java index f846de5e5..8513353dc 100644 --- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java +++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java @@ -21,7 +21,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 65f9d6bc0..8fddf3cff 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -86,7 +86,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch // Type of popups which should be kept open during launcher rebind public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET - | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; // Usually we show the back button when a floating view is open. Instead, hide for these types. public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 9724869bf..9db694944 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -43,6 +43,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; @@ -72,7 +73,7 @@ public class AutoInstallsLayout { static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback) { - Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk( + Pair<String, Resources> customizationApkInfo = PackageManagerHelper.findSystemApk( ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager()); if (customizationApkInfo == null) { return null; diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 50553095d..8de00691e 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -34,9 +34,9 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.shortcuts.DeepShortcutManager; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.uioverrides.DisplayRotationListener; import com.android.launcher3.uioverrides.WallpaperColorInfo; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Themes; import androidx.annotation.Nullable; @@ -120,6 +120,10 @@ public abstract class BaseDraggingActivity extends BaseActivity public abstract View getRootView(); + public void returnToHomescreen() { + // no-op + } + public Rect getViewBounds(View v) { int[] pos = new int[2]; v.getLocationOnScreen(pos); @@ -135,7 +139,7 @@ public abstract class BaseDraggingActivity extends BaseActivity public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item, @Nullable String sourceContainer) { - if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { + if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); return false; } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index e9b932aaf..a6b53b92d 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -612,7 +612,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // Already an activity target return original; } - if (!Utilities.isLauncherAppTarget(original.launchIntent)) { + if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) { return original; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 03fdc971d..40d76681b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -20,6 +20,8 @@ import static android.content.pm.ActivityInfo.CONFIG_LOCALE; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; +import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; @@ -1426,9 +1428,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, outState.remove(RUNTIME_STATE_WIDGET_PANEL); } - // We close any open folders and shortcut containers since they will not be re-opened, + // We close any open folders and shortcut containers that are not safe for rebind, // and we need to make sure this state is reflected. - AbstractFloatingView.closeAllOpenViews(this, false); + AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE); + finishAutoCancelActionMode(); if (mPendingRequestArgs != null) { outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); @@ -1909,8 +1912,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Floating panels (except the full widget sheet) are associated with individual icons. If // we are starting a fresh bind, close all such panels as all the icons are about // to go away. - AbstractFloatingView.closeOpenViews(this, true, - AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); + AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE); setWorkspaceLoading(true); @@ -2566,6 +2568,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return (Launcher) fromContext(context); } + @Override + public void returnToHomescreen() { + super.returnToHomescreen(); + getStateManager().goToState(LauncherState.NORMAL); + } + /** * Just a wrapper around the type cast to allow easier tracking of calls. */ diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 6ad5c3684..6081300ce 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -33,7 +33,6 @@ import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; import android.content.res.Resources; import android.database.Cursor; @@ -50,7 +49,6 @@ import android.os.Handler; import android.os.Message; import android.os.Process; import android.os.UserHandle; -import android.os.UserManager; import android.provider.BaseColumns; import android.provider.Settings; import android.text.TextUtils; @@ -70,6 +68,7 @@ import com.android.launcher3.util.IOUtils; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.NoLocaleSQLiteHelper; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Thunk; @@ -77,7 +76,6 @@ import org.xmlpull.v1.XmlPullParser; import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; @@ -873,7 +871,7 @@ public class LauncherProvider extends ContentProvider { continue; } - if (!Utilities.isLauncherAppTarget(intent)) { + if (!PackageManagerHelper.isLauncherAppTarget(intent)) { continue; } diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java index 380078b26..af5402ae2 100644 --- a/src/com/android/launcher3/Partner.java +++ b/src/com/android/launcher3/Partner.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import static com.android.launcher3.util.PackageManagerHelper.findSystemApk; + import android.content.pm.PackageManager; import android.content.res.Resources; import android.util.DisplayMetrics; @@ -59,7 +61,7 @@ public class Partner { */ public static synchronized Partner get(PackageManager pm) { if (!sSearched) { - Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm); + Pair<String, Resources> apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm); if (apkInfo != null) { sPartner = new Partner(apkInfo.first, apkInfo.second); } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index fc5cd8a88..80305228b 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -23,15 +23,9 @@ import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.WallpaperManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; @@ -46,7 +40,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.Message; @@ -59,7 +52,6 @@ import android.text.TextUtils; import android.text.style.TtsSpan; import android.util.DisplayMetrics; import android.util.Log; -import android.util.Pair; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; @@ -68,7 +60,6 @@ import android.view.animation.Interpolator; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.TintedDrawableSpan; @@ -80,13 +71,10 @@ import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; -import java.io.Closeable; -import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.StringTokenizer; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -243,7 +231,6 @@ public final class Utilities { return scale; } - /** * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}. */ @@ -379,53 +366,6 @@ public final class Utilities { return min + (value * (max - min)); } - public static boolean isSystemApp(Context context, Intent intent) { - PackageManager pm = context.getPackageManager(); - ComponentName cn = intent.getComponent(); - String packageName = null; - if (cn == null) { - ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - if ((info != null) && (info.activityInfo != null)) { - packageName = info.activityInfo.packageName; - } - } else { - packageName = cn.getPackageName(); - } - if (packageName != null) { - try { - PackageInfo info = pm.getPackageInfo(packageName, 0); - return (info != null) && (info.applicationInfo != null) && - ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - } catch (NameNotFoundException e) { - return false; - } - } else { - return false; - } - } - - /* - * Finds a system apk which had a broadcast receiver listening to a particular action. - * @param action intent action used to find the apk - * @return a pair of apk package name and the resources. - */ - static Pair<String, Resources> findSystemApk(String action, PackageManager pm) { - final Intent intent = new Intent(action); - for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { - if (info.activityInfo != null && - (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - final String packageName = info.activityInfo.packageName; - try { - final Resources res = pm.getResourcesForApplication(packageName); - return Pair.create(packageName, res); - } catch (NameNotFoundException e) { - Log.w(TAG, "Failed to find resources for " + packageName); - } - } - } - return null; - } - /** * Trims the string, removing all whitespace at the beginning and end of the string. * Non-breaking whitespaces are also removed. @@ -450,51 +390,10 @@ public final class Utilities { return (int) Math.ceil(fm.bottom - fm.top); } - /** - * Convenience println with multiple args. - */ - public static void println(String key, Object... args) { - StringBuilder b = new StringBuilder(); - b.append(key); - b.append(": "); - boolean isFirstArgument = true; - for (Object arg : args) { - if (isFirstArgument) { - isFirstArgument = false; - } else { - b.append(", "); - } - b.append(arg); - } - System.out.println(b.toString()); - } - public static boolean isRtl(Resources res) { return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } - /** - * Returns true if the intent is a valid launch intent for a launcher activity of an app. - * This is used to identify shortcuts which are different from the ones exposed by the - * applications' manifest file. - * - * @param launchIntent The intent that will be launched when the shortcut is clicked. - */ - public static boolean isLauncherAppTarget(Intent launchIntent) { - if (launchIntent != null - && Intent.ACTION_MAIN.equals(launchIntent.getAction()) - && launchIntent.getComponent() != null - && launchIntent.getCategories() != null - && launchIntent.getCategories().size() == 1 - && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) - && TextUtils.isEmpty(launchIntent.getDataString())) { - // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. - Bundle extras = launchIntent.getExtras(); - return extras == null || extras.keySet().isEmpty(); - } - return false; - } - public static float dpiFromPx(int size, DisplayMetrics metrics){ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; return (size / densityRatio); @@ -597,18 +496,6 @@ public final class Utilities { return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed(); } - public static void closeSilently(Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException e) { - if (FeatureFlags.IS_DOGFOOD_BUILD) { - Log.d(TAG, "Error closing", e); - } - } - } - } - public static boolean isBinderSizeError(Exception e) { return e.getCause() instanceof TransactionTooLargeException || e.getCause() instanceof DeadObjectException; @@ -723,25 +610,6 @@ public final class Utilities { } } - public static int[] getIntArrayFromString(String tokenized) { - StringTokenizer tokenizer = new StringTokenizer(tokenized, ","); - int[] array = new int[tokenizer.countTokens()]; - int count = 0; - while (tokenizer.hasMoreTokens()) { - array[count] = Integer.parseInt(tokenizer.nextToken()); - count++; - } - return array; - } - - public static String getStringFromIntArray(int[] array) { - StringBuilder str = new StringBuilder(); - for (int value : array) { - str.append(value).append(","); - } - return str.toString(); - } - public static float squaredHypot(float x, float y) { return x * x + y * y; } diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java index f7f8ef18f..cc920767f 100644 --- a/src/com/android/launcher3/logging/FileLog.java +++ b/src/com/android/launcher3/logging/FileLog.java @@ -8,6 +8,7 @@ import android.util.Pair; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.IOUtils; import java.io.BufferedReader; import java.io.File; @@ -131,7 +132,7 @@ public final class FileLog { private PrintWriter mCurrentWriter = null; private void closeWriter() { - Utilities.closeSilently(mCurrentWriter); + IOUtils.closeSilently(mCurrentWriter); mCurrentWriter = null; } @@ -219,7 +220,7 @@ public final class FileLog { } catch (Exception e) { // ignore } finally { - Utilities.closeSilently(in); + IOUtils.closeSilently(in); } } } diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index ed0d47080..8c264c186 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -31,9 +31,9 @@ import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherSettings; import com.android.launcher3.WorkspaceItemInfo; -import com.android.launcher3.Utilities; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.PackageManagerHelper; import java.util.ArrayList; import java.util.List; @@ -162,7 +162,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { intentWithoutPkg = intent.toUri(0); } - boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent); + boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent); synchronized (dataModel) { for (ItemInfo item : dataModel.itemsIdMap) { if (item instanceof WorkspaceItemInfo) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 0138572d0..6c3394669 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -20,6 +20,7 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems; +import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; @@ -69,6 +70,7 @@ import com.android.launcher3.provider.ImportDataTask; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.IOUtils; import com.android.launcher3.util.LooperIdleLock; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; @@ -317,9 +319,9 @@ public class LoaderTask implements Runnable { // We can only query for shortcuts when the user is unlocked. if (userUnlocked) { - List<ShortcutInfo> pinnedShortcuts = + DeepShortcutManager.QueryResult pinnedShortcuts = mShortcutManager.queryForPinnedShortcuts(null, user); - if (mShortcutManager.wasLastCallSuccess()) { + if (pinnedShortcuts.wasSuccess()) { for (ShortcutInfo shortcut : pinnedShortcuts) { shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); @@ -531,7 +533,7 @@ public class LoaderTask implements Runnable { info.spanX = 1; info.spanY = 1; info.runtimeStatusFlags |= disabledState; - if (isSafeMode && !Utilities.isSystemApp(context, intent)) { + if (isSafeMode && !isSystemApp(context, intent)) { info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; } @@ -703,7 +705,7 @@ public class LoaderTask implements Runnable { } } } finally { - Utilities.closeSilently(c); + IOUtils.closeSilently(c); } // Break early if we've stopped loading diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index 2cb256e09..50fff2653 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; /** * Task to handle changing of lock state of the user @@ -58,9 +57,9 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>(); if (isUserUnlocked) { - List<ShortcutInfo> shortcuts = + DeepShortcutManager.QueryResult shortcuts = deepShortcutManager.queryForPinnedShortcuts(null, mUser); - if (deepShortcutManager.wasLastCallSuccess()) { + if (shortcuts.wasSuccess()) { for (ShortcutInfo shortcut : shortcuts) { pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); } diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java index 7b62f53fe..970a03e80 100644 --- a/src/com/android/launcher3/provider/ImportDataTask.java +++ b/src/com/android/launcher3/provider/ImportDataTask.java @@ -42,7 +42,6 @@ import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -50,6 +49,7 @@ import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSparseArrayMap; +import com.android.launcher3.util.PackageManagerHelper; import java.net.URISyntaxException; import java.util.ArrayList; @@ -223,7 +223,7 @@ public class ImportDataTask { case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_APPLICATION: { intent = Intent.parseUri(c.getString(intentIndex), 0); - if (Utilities.isLauncherAppTarget(intent)) { + if (PackageManagerHelper.isLauncherAppTarget(intent)) { type = Favorites.ITEM_TYPE_APPLICATION; } else { values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 3c0c5fdde..0a2d4e3d3 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -16,8 +16,6 @@ package com.android.launcher3.provider; -import static com.android.launcher3.Utilities.getIntArrayFromString; -import static com.android.launcher3.Utilities.getStringFromIntArray; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import android.app.backup.BackupManager; @@ -40,6 +38,7 @@ import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Utilities; import com.android.launcher3.logging.FileLog; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; +import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LogConfig; import java.io.InvalidObjectException; @@ -246,8 +245,8 @@ public class RestoreDbTask { SharedPreferences prefs = Utilities.getPrefs(context); if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) { AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, - getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")), - getIntArrayFromString(prefs.getString(APPWIDGET_IDS, ""))); + IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(), + IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray()); } else { FileLog.d(TAG, "No app widget ids to restore."); } @@ -259,8 +258,8 @@ public class RestoreDbTask { public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds, @NonNull int[] newIds) { Utilities.getPrefs(context).edit() - .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds)) - .putString(APPWIDGET_IDS, getStringFromIntArray(newIds)) + .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString()) + .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString()) .commit(); } diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java index f95f74d60..4a4a5ca22 100644 --- a/src/com/android/launcher3/util/IOUtils.java +++ b/src/com/android/launcher3/util/IOUtils.java @@ -17,10 +17,13 @@ package com.android.launcher3.util; import android.content.Context; +import android.util.Log; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -35,6 +38,7 @@ import java.util.UUID; public class IOUtils { private static final int BUF_SIZE = 0x1000; // 4K + private static final String TAG = "IOUtils"; public static byte[] toByteArray(File file) throws IOException { try (InputStream in = new FileInputStream(file)) { @@ -77,4 +81,16 @@ public class IOUtils { } return file.getAbsolutePath(); } + + public static void closeSilently(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + if (FeatureFlags.IS_DOGFOOD_BUILD) { + Log.d(TAG, "Error closing", e); + } + } + } + } } diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java index d2a551f45..7252f7ac1 100644 --- a/src/com/android/launcher3/util/IntArray.java +++ b/src/com/android/launcher3/util/IntArray.java @@ -17,6 +17,7 @@ package com.android.launcher3.util; import java.util.Arrays; +import java.util.StringTokenizer; /** * Copy of the platform hidden implementation of android.util.IntArray. @@ -248,6 +249,17 @@ public class IntArray implements Cloneable { return b.toString(); } + public static IntArray fromConcatString(String concatString) { + StringTokenizer tokenizer = new StringTokenizer(concatString, ","); + int[] array = new int[tokenizer.countTokens()]; + int count = 0; + while (tokenizer.hasMoreTokens()) { + array[count] = Integer.parseInt(tokenizer.nextToken().trim()); + count++; + } + return new IntArray(array, array.length); + } + /** * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds. * diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index 7d3a94162..ef4307ec5 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -24,9 +24,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -35,6 +37,7 @@ import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.widget.Toast; import com.android.launcher3.AppInfo; @@ -220,4 +223,73 @@ public class PackageManagerHelper { packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL); return packageFilter; } + + public static boolean isSystemApp(Context context, Intent intent) { + PackageManager pm = context.getPackageManager(); + ComponentName cn = intent.getComponent(); + String packageName = null; + if (cn == null) { + ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + if ((info != null) && (info.activityInfo != null)) { + packageName = info.activityInfo.packageName; + } + } else { + packageName = cn.getPackageName(); + } + if (packageName != null) { + try { + PackageInfo info = pm.getPackageInfo(packageName, 0); + return (info != null) && (info.applicationInfo != null) && + ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + } catch (NameNotFoundException e) { + return false; + } + } else { + return false; + } + } + + /** + * Finds a system apk which had a broadcast receiver listening to a particular action. + * @param action intent action used to find the apk + * @return a pair of apk package name and the resources. + */ + public static Pair<String, Resources> findSystemApk(String action, PackageManager pm) { + final Intent intent = new Intent(action); + for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { + if (info.activityInfo != null && + (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + final String packageName = info.activityInfo.packageName; + try { + final Resources res = pm.getResourcesForApplication(packageName); + return Pair.create(packageName, res); + } catch (NameNotFoundException e) { + Log.w(TAG, "Failed to find resources for " + packageName); + } + } + } + return null; + } + + /** + * Returns true if the intent is a valid launch intent for a launcher activity of an app. + * This is used to identify shortcuts which are different from the ones exposed by the + * applications' manifest file. + * + * @param launchIntent The intent that will be launched when the shortcut is clicked. + */ + public static boolean isLauncherAppTarget(Intent launchIntent) { + if (launchIntent != null + && Intent.ACTION_MAIN.equals(launchIntent.getAction()) + && launchIntent.getComponent() != null + && launchIntent.getCategories() != null + && launchIntent.getCategories().size() == 1 + && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) + && TextUtils.isEmpty(launchIntent.getDataString())) { + // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. + Bundle extras = launchIntent.getExtras(); + return extras == null || extras.keySet().isEmpty(); + } + return false; + } } diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java index 6b6f70d7b..84a59b228 100644 --- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -45,19 +45,15 @@ public class DeepShortcutManager { | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED; private static DeepShortcutManager sInstance; - private static final Object sInstanceLock = new Object(); public static DeepShortcutManager getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new DeepShortcutManager(context.getApplicationContext()); - } - return sInstance; + if (sInstance == null) { + sInstance = new DeepShortcutManager(context.getApplicationContext()); } + return sInstance; } private final LauncherApps mLauncherApps; - private boolean mWasLastCallSuccess; private DeepShortcutManager(Context context) { mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); @@ -70,17 +66,13 @@ public class DeepShortcutManager { && !info.isDisabled() && !isItemPromise; } - public boolean wasLastCallSuccess() { - return mWasLastCallSuccess; - } - /** * Queries for the shortcuts with the package name and provided ids. * * This method is intended to get the full details for shortcuts when they are added or updated, * because we only get "key" fields in onShortcutsChanged(). */ - public List<ShortcutInfo> queryForFullDetails(String packageName, + public QueryResult queryForFullDetails(String packageName, List<String> shortcutIds, UserHandle user) { return query(FLAG_GET_ALL, packageName, null, shortcutIds, user); } @@ -89,7 +81,7 @@ public class DeepShortcutManager { * Gets all the manifest and dynamic shortcuts associated with the given package and user, * to be displayed in the shortcuts container on long press. */ - public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity, + public QueryResult queryForShortcutsContainer(ComponentName activity, UserHandle user) { return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC, activity.getPackageName(), activity, null, user); @@ -107,10 +99,8 @@ public class DeepShortcutManager { pinnedIds.remove(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to unpin shortcut", e); - mWasLastCallSuccess = false; } } @@ -126,10 +116,8 @@ public class DeepShortcutManager { pinnedIds.add(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to pin shortcut", e); - mWasLastCallSuccess = false; } } @@ -138,23 +126,18 @@ public class DeepShortcutManager { try { mLauncherApps.startShortcut(packageName, id, sourceBounds, startActivityOptions, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to start shortcut", e); - mWasLastCallSuccess = false; } } public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) { try { - Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density); - mWasLastCallSuccess = true; - return icon; + return mLauncherApps.getShortcutIconDrawable(shortcutInfo, density); } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to get shortcut icon", e); - mWasLastCallSuccess = false; + return null; } - return null; } /** @@ -162,20 +145,20 @@ public class DeepShortcutManager { * * If packageName is null, returns all pinned shortcuts regardless of package. */ - public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) { + public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) { return queryForPinnedShortcuts(packageName, null, user); } - public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, - List<String> shortcutIds, UserHandle user) { + public QueryResult queryForPinnedShortcuts(String packageName, List<String> shortcutIds, + UserHandle user) { return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user); } - public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) { + public QueryResult queryForAllShortcuts(UserHandle user) { return query(FLAG_GET_ALL, null, null, null, user); } - private List<String> extractIds(List<ShortcutInfo> shortcuts) { + private static List<String> extractIds(List<ShortcutInfo> shortcuts) { List<String> shortcutIds = new ArrayList<>(shortcuts.size()); for (ShortcutInfo shortcut : shortcuts) { shortcutIds.add(shortcut.getId()); @@ -189,8 +172,8 @@ public class DeepShortcutManager { * * TODO: Use the cache to optimize this so we don't make an RPC every time. */ - private List<ShortcutInfo> query(int flags, String packageName, - ComponentName activity, List<String> shortcutIds, UserHandle user) { + private QueryResult query(int flags, String packageName, ComponentName activity, + List<String> shortcutIds, UserHandle user) { ShortcutQuery q = new ShortcutQuery(); q.setQueryFlags(flags); if (packageName != null) { @@ -198,18 +181,12 @@ public class DeepShortcutManager { q.setActivity(activity); q.setShortcutIds(shortcutIds); } - List<ShortcutInfo> shortcutInfos = null; try { - shortcutInfos = mLauncherApps.getShortcuts(q, user); - mWasLastCallSuccess = true; + return new QueryResult(mLauncherApps.getShortcuts(q, user)); } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to query for shortcuts", e); - mWasLastCallSuccess = false; + return QueryResult.FAILURE; } - if (shortcutInfos == null) { - return Collections.EMPTY_LIST; - } - return shortcutInfos; } public boolean hasHostPermission() { @@ -220,4 +197,25 @@ public class DeepShortcutManager { } return false; } + + public static class QueryResult extends ArrayList<ShortcutInfo> { + + static QueryResult FAILURE = new QueryResult(); + + private final boolean mWasSuccess; + + QueryResult(List<ShortcutInfo> result) { + super(result == null ? Collections.emptyList() : result); + mWasSuccess = true; + } + + QueryResult() { + mWasSuccess = false; + } + + + public boolean wasSuccess() { + return mWasSuccess; + } + } } diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 7d60ad65b..64df8e0e9 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -15,9 +15,9 @@ import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.util.PackageManagerHelper; import org.junit.Before; import org.junit.Test; @@ -115,7 +115,7 @@ public class LoaderCursorTest { WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo( new Intent().setComponent(cn), false /* allowMissingTarget */, true); assertNotNull(info); - assertTrue(Utilities.isLauncherAppTarget(info.intent)); + assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent)); } @Test @@ -127,7 +127,7 @@ public class LoaderCursorTest { WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo( new Intent().setComponent(cn), true /* allowMissingTarget */, true); assertNotNull(info); - assertTrue(Utilities.isLauncherAppTarget(info.intent)); + assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent)); } @Test |