Merge "Add StatsLog*Manager for logging. Bug: 113043444" into ub-launcher3-master
diff --git a/.gitignore b/.gitignore
index 7240e48..694b40c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,5 @@
local.properties
gradle/
build/
-gradlew*
\ No newline at end of file
+gradlew*
+.DS_Store
diff --git a/Android.bp b/Android.bp
index e3dd5e5..c583244 100644
--- a/Android.bp
+++ b/Android.bp
@@ -29,23 +29,3 @@
],
platform_apis: true,
}
-
-
-android_library {
- name: "icon-loader",
- sdk_version: "28",
- static_libs: [
- "androidx.core_core",
- ],
- resource_dirs: [
- "res",
- ],
- srcs: [
- "src/com/android/launcher3/icons/BaseIconFactory.java",
- "src/com/android/launcher3/icons/BitmapInfo.java",
- "src/com/android/launcher3/icons/IconNormalizer.java",
- "src/com/android/launcher3/icons/FixedScaleDrawable.java",
- "src/com/android/launcher3/icons/ShadowGenerator.java",
- "src/com/android/launcher3/icons/ColorExtractor.java",
- ],
-}
diff --git a/Android.mk b/Android.mk
index fbe19b0..9d6e629 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,7 +67,8 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.recyclerview_recyclerview \
androidx.dynamicanimation_dynamicanimation \
- androidx.preference_preference
+ androidx.preference_preference \
+ iconloader
LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
@@ -101,6 +102,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
+ $(call all-java-files-under, src_shortcuts_overrides) \
$(call all-java-files-under, src_ui_overrides) \
$(call all-java-files-under, src_flags)
@@ -131,7 +133,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_ui_overrides) \
- $(call all-java-files-under, go/src_flags)
+ $(call all-java-files-under, go/src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res
@@ -174,7 +176,8 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, src_flags)
+ $(call all-java-files-under, src_flags) \
+ $(call all-java-files-under, src_shortcuts_overrides)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
LOCAL_PROGUARD_ENABLED := disabled
@@ -235,7 +238,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, go/src_flags)
+ $(call all-java-files-under, go/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
diff --git a/build.gradle b/build.gradle
index 1b9df53..33409c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,19 +4,17 @@
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
+ classpath GRADLE_CLASS_PATH
+ classpath PROTOBUF_CLASS_PATH
}
}
-final String SUPPORT_LIBS_VERSION = '1.0.0-alpha1'
-
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
+ compileSdkVersion COMPILE_SDK.toInteger()
+ buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion 21
@@ -72,7 +70,7 @@
sourceSets {
main {
res.srcDirs = ['res']
- java.srcDirs = ['src']
+ java.srcDirs = ['src', 'src_shortcuts_overrides']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
srcDir 'protos/'
@@ -100,7 +98,7 @@
l3go {
res.srcDirs = ['go/res']
- java.srcDirs = ['go/src_flags', "src_ui_overrides"]
+ java.srcDirs = ['go/src', "src_ui_overrides"]
manifest.srcFile "go/AndroidManifest.xml"
}
@@ -120,9 +118,15 @@
}
dependencies {
- implementation "androidx.dynamicanimation:dynamicanimation:${SUPPORT_LIBS_VERSION}"
- implementation "androidx.recyclerview:recyclerview:${SUPPORT_LIBS_VERSION}"
- implementation 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
+ implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"
+ implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
+ implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
+ implementation PROTOBUF_DEPENDENCY
+ implementation project(':IconLoader')
+
+ // This is already included in sysui_shared
+ aospImplementation fileTree(dir: "libs", include: 'plugin_core.jar')
+ l3goImplementation fileTree(dir: "libs", include: 'plugin_core.jar')
quickstepImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
@@ -133,7 +137,7 @@
androidTestImplementation 'com.android.support.test:runner:1.0.0'
androidTestImplementation 'com.android.support.test:rules:1.0.0'
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
- androidTestImplementation "androidx.annotation:annotation:${SUPPORT_LIBS_VERSION}"
+ androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}"
}
protobuf {
diff --git a/go/src_flags/com/android/launcher3/config/FeatureFlags.java b/go/src/com/android/launcher3/config/FeatureFlags.java
similarity index 100%
rename from go/src_flags/com/android/launcher3/config/FeatureFlags.java
rename to go/src/com/android/launcher3/config/FeatureFlags.java
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
new file mode 100644
index 0000000..18f3f9d
--- /dev/null
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -0,0 +1,67 @@
+/*
+ * 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.launcher3.model;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+ private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
+
+ /**
+ * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
+ * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
+ * is not sorted. This list is sorted at the UI when using
+ * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+ *
+ * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+ */
+ public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+ return EMPTY_WIDGET_LIST;
+ }
+
+ /**
+ * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
+ * only widgets and shortcuts associated with the package/user are.
+ */
+ public List<ComponentWithLabel> update(LauncherAppState app,
+ @Nullable PackageUserKey packageUser) {
+ return Collections.emptyList();
+ }
+
+
+ public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
+ LauncherAppState app) {
+ }
+}
\ No newline at end of file
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
new file mode 100644
index 0000000..ff0c907
--- /dev/null
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -0,0 +1,124 @@
+/*
+ * 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.launcher3.shortcuts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Collections;
+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();
+
+ public static DeepShortcutManager getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new DeepShortcutManager(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+ }
+
+ private DeepShortcutManager(Context context) {
+ }
+
+ public static boolean supportsShortcuts(ItemInfo info) {
+ return false;
+ }
+
+ public boolean wasLastCallSuccess() {
+ return false;
+ }
+
+ public void onShortcutsChanged(List<ShortcutInfoCompat> 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<ShortcutInfoCompat> queryForFullDetails(String packageName,
+ List<String> shortcutIds, UserHandle user) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * 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<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+ UserHandle user) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Removes the given shortcut from the current list of pinned shortcuts.
+ * (Runs on background thread)
+ */
+ public void unpinShortcut(final ShortcutKey key) {
+ }
+
+ /**
+ * Adds the given shortcut to the current list of pinned shortcuts.
+ * (Runs on background thread)
+ */
+ public void pinShortcut(final ShortcutKey key) {
+ }
+
+ public void startShortcut(String packageName, String id, Rect sourceBounds,
+ Bundle startActivityOptions, UserHandle user) {
+ }
+
+ public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+ return null;
+ }
+
+ /**
+ * Returns the id's of pinned shortcuts associated with the given package and user.
+ *
+ * If packageName is null, returns all pinned shortcuts regardless of package.
+ */
+ public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
+ return Collections.emptyList();
+ }
+
+ public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+ List<String> shortcutIds, UserHandle user) {
+ return Collections.emptyList();
+ }
+
+ public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
+ return Collections.emptyList();
+ }
+
+ public boolean hasHostPermission() {
+ return false;
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..b299cfe
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,13 @@
+# Until all the dependencies move to android X
+android.useAndroidX = true
+android.enableJetifier = true
+
+ANDROID_X_VERSION=1.0.0-beta01
+
+GRADLE_CLASS_PATH=com.android.tools.build:gradle:3.2.0-rc03
+
+PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.6
+PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
+
+BUILD_TOOLS_VERSION=28.0.3
+COMPILE_SDK=28
\ No newline at end of file
diff --git a/iconloaderlib/Android.bp b/iconloaderlib/Android.bp
new file mode 100644
index 0000000..8a71f94
--- /dev/null
+++ b/iconloaderlib/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_library {
+ name: "iconloader",
+ sdk_version: "28",
+ min_sdk_version: "21",
+ static_libs: [
+ "androidx.core_core",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+}
diff --git a/iconloaderlib/AndroidManifest.xml b/iconloaderlib/AndroidManifest.xml
new file mode 100644
index 0000000..b30258d
--- /dev/null
+++ b/iconloaderlib/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.launcher3.icons">
+</manifest>
diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle
new file mode 100644
index 0000000..d080293
--- /dev/null
+++ b/iconloaderlib/build.gradle
@@ -0,0 +1,50 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ google()
+ }
+ dependencies {
+ classpath GRADLE_CLASS_PATH
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion COMPILE_SDK.toInteger()
+ buildToolsVersion BUILD_TOOLS_VERSION
+ publishNonDefault true
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ }
+
+ sourceSets {
+ main {
+ java.srcDirs = ['src']
+ manifest.srcFile 'AndroidManifest.xml'
+ res.srcDirs = ['res']
+ }
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+ }
+}
+
+
+repositories {
+ mavenCentral()
+ google()
+}
+
+dependencies {
+ implementation "androidx.core:core:${ANDROID_X_VERSION}"
+}
diff --git a/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
similarity index 100%
rename from res/drawable-v26/adaptive_icon_drawable_wrapper.xml
rename to iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
diff --git a/res/drawable/ic_instant_app_badge.xml b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
similarity index 93%
rename from res/drawable/ic_instant_app_badge.xml
rename to iconloaderlib/res/drawable/ic_instant_app_badge.xml
index cc53230..b74317e 100644
--- a/res/drawable/ic_instant_app_badge.xml
+++ b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
@@ -21,23 +21,19 @@
<path
android:fillColor="@android:color/black"
- android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@android:color/white"
- android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@android:color/white"
- android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@android:color/black"
android:fillAlpha="0.87"
- android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
-</vector>
\ No newline at end of file
+</vector>
diff --git a/iconloaderlib/res/values/colors.xml b/iconloaderlib/res/values/colors.xml
new file mode 100644
index 0000000..873b2fc
--- /dev/null
+++ b/iconloaderlib/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <color name="legacy_icon_background">#FFFFFF</color>
+</resources>
diff --git a/iconloaderlib/res/values/dimens.xml b/iconloaderlib/res/values/dimens.xml
new file mode 100644
index 0000000..e8c0c44
--- /dev/null
+++ b/iconloaderlib/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <dimen name="profile_badge_size">24dp</dimen>
+</resources>
diff --git a/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android.launcher3/icons/BaseIconFactory.java
similarity index 99%
rename from src/com/android/launcher3/icons/BaseIconFactory.java
rename to iconloaderlib/src/com/android.launcher3/icons/BaseIconFactory.java
index cd60de5..681c03c 100644
--- a/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android.launcher3/icons/BaseIconFactory.java
@@ -18,7 +18,7 @@
import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.R;
+import com.android.launcher3.icons.R;
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
diff --git a/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android.launcher3/icons/BitmapInfo.java
similarity index 100%
rename from src/com/android/launcher3/icons/BitmapInfo.java
rename to iconloaderlib/src/com/android.launcher3/icons/BitmapInfo.java
diff --git a/src/com/android/launcher3/icons/ColorExtractor.java b/iconloaderlib/src/com/android.launcher3/icons/ColorExtractor.java
similarity index 100%
rename from src/com/android/launcher3/icons/ColorExtractor.java
rename to iconloaderlib/src/com/android.launcher3/icons/ColorExtractor.java
diff --git a/src/com/android/launcher3/icons/FixedScaleDrawable.java b/iconloaderlib/src/com/android.launcher3/icons/FixedScaleDrawable.java
similarity index 100%
rename from src/com/android/launcher3/icons/FixedScaleDrawable.java
rename to iconloaderlib/src/com/android.launcher3/icons/FixedScaleDrawable.java
diff --git a/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android.launcher3/icons/IconNormalizer.java
similarity index 98%
rename from src/com/android/launcher3/icons/IconNormalizer.java
rename to iconloaderlib/src/com/android.launcher3/icons/IconNormalizer.java
index 8eb8252..05908df 100644
--- a/src/com/android/launcher3/icons/IconNormalizer.java
+++ b/iconloaderlib/src/com/android.launcher3/icons/IconNormalizer.java
@@ -20,9 +20,6 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
diff --git a/src/com/android/launcher3/icons/ShadowGenerator.java b/iconloaderlib/src/com/android.launcher3/icons/ShadowGenerator.java
similarity index 100%
rename from src/com/android/launcher3/icons/ShadowGenerator.java
rename to iconloaderlib/src/com/android.launcher3/icons/ShadowGenerator.java
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 5680a67..d7bbfe0 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -178,6 +178,14 @@
@Override
public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
if (hasControlRemoteAppTransitionPermission()) {
+ boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
+ && findTaskViewToLaunch(launcher, v, null) != null;
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ if (fromRecents && recentsView.getQuickScrubController().isQuickSwitch()) {
+ return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
+ R.anim.no_anim);
+ }
+
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
true /* startAtFrontOfQueue */) {
@@ -218,8 +226,6 @@
}
};
- boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
- && findTaskViewToLaunch(launcher, v, null) != null;
int duration = fromRecents
? RECENTS_LAUNCH_DURATION
: APP_LAUNCH_DURATION;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index 2645302..1d65a54 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -35,6 +35,7 @@
* Vertical transition of the task previews relative to the full container.
*/
public static final float OVERVIEW_TRANSLATION_FACTOR = 0.4f;
+ public static final float OVERVIEW_CENTERED_TRANSLATION_FACTOR = 0.5f;
private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION
| FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
@@ -60,12 +61,17 @@
RecentsView recentsView = launcher.getOverviewPanel();
recentsView.getTaskSize(sTempRect);
- return new float[] {getOverviewScale(launcher.getDeviceProfile(), sTempRect, launcher),
- OVERVIEW_TRANSLATION_FACTOR};
+ boolean isQuickSwitch = recentsView.getQuickScrubController().isQuickSwitch();
+ float translationYFactor = isQuickSwitch
+ ? OVERVIEW_CENTERED_TRANSLATION_FACTOR
+ : OVERVIEW_TRANSLATION_FACTOR;
+ return new float[] {getOverviewScale(launcher.getDeviceProfile(), sTempRect, launcher,
+ isQuickSwitch), translationYFactor};
}
- public static float getOverviewScale(DeviceProfile dp, Rect taskRect, Context context) {
- if (dp.isVerticalBarLayout()) {
+ public static float getOverviewScale(DeviceProfile dp, Rect taskRect, Context context,
+ boolean isQuickSwitch) {
+ if (dp.isVerticalBarLayout() && !isQuickSwitch) {
return 1f;
}
@@ -73,6 +79,10 @@
float usedHeight = taskRect.height() + res.getDimension(R.dimen.task_thumbnail_top_margin);
float usedWidth = taskRect.width() + 2 * (res.getDimension(R.dimen.recents_page_spacing)
+ res.getDimension(R.dimen.quickscrub_adjacent_visible_width));
+ if (isQuickSwitch) {
+ usedWidth = taskRect.width();
+ return Math.max(dp.availableHeightPx / usedHeight, dp.availableWidthPx / usedWidth);
+ }
return Math.min(Math.min(dp.availableHeightPx / usedHeight,
dp.availableWidthPx / usedWidth), MAX_PREVIEW_SCALE_UP);
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index c809e28..85eed1f 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -16,7 +16,6 @@
package com.android.quickstep;
import static android.view.View.TRANSLATION_Y;
-
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -58,6 +57,7 @@
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.uioverrides.FastOverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -192,7 +192,8 @@
@InteractionType int interactionType, TransformedRect outRect) {
LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect);
if (interactionType == INTERACTION_QUICK_SCRUB) {
- outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context);
+ outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context,
+ FeatureFlags.QUICK_SWITCH.get());
}
if (dp.isVerticalBarLayout()) {
Rect targetInsets = dp.getInsets();
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 3420767..c44ccd3 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -16,8 +16,18 @@
package com.android.quickstep;
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.util.FloatProperty;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.animation.Interpolator;
@@ -37,8 +47,10 @@
* The behavior is to evenly divide the progress into sections, each of which scrolls one page.
* The first and last section set an alarm to auto-advance backwards or forwards, respectively.
*/
+@TargetApi(Build.VERSION_CODES.P)
public class QuickScrubController implements OnAlarmListener {
+ public static final int QUICK_SWITCH_FROM_APP_START_DURATION = 0;
public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 200;
// We want the translation y to finish faster than the rest of the animation.
@@ -52,6 +64,19 @@
0.05f, 0.20f, 0.35f, 0.50f, 0.65f, 0.80f, 0.95f
};
+ private static final FloatProperty<QuickScrubController> PROGRESS
+ = new FloatProperty<QuickScrubController>("progress") {
+ @Override
+ public void setValue(QuickScrubController quickScrubController, float progress) {
+ quickScrubController.onQuickScrubProgress(progress);
+ }
+
+ @Override
+ public Float get(QuickScrubController quickScrubController) {
+ return quickScrubController.mEndProgress;
+ }
+ };
+
private static final String TAG = "QuickScrubController";
private static final boolean ENABLE_AUTO_ADVANCE = true;
private static final long AUTO_ADVANCE_DELAY = 500;
@@ -72,6 +97,13 @@
private ActivityControlHelper mActivityControlHelper;
private TouchInteractionLog mTouchInteractionLog;
+ private boolean mIsQuickSwitch;
+ private float mStartProgress;
+ private float mEndProgress;
+ private float mPrevProgressDelta;
+ private float mPrevPrevProgressDelta;
+ private boolean mShouldSwitchToNext;
+
public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
mActivity = activity;
mRecentsView = recentsView;
@@ -91,17 +123,26 @@
mActivityControlHelper = controlHelper;
mTouchInteractionLog = touchInteractionLog;
+ if (mIsQuickSwitch) {
+ mShouldSwitchToNext = true;
+ mPrevProgressDelta = 0;
+ if (mRecentsView.getTaskViewCount() > 0) {
+ mRecentsView.getTaskViewAt(0).setFullscreen(true);
+ }
+ if (mRecentsView.getTaskViewCount() > 1) {
+ mRecentsView.getTaskViewAt(1).setFullscreen(true);
+ }
+ }
+
snapToNextTaskIfAvailable();
mActivity.getUserEventDispatcher().resetActionDurationMillis();
}
public void onQuickScrubEnd() {
mInQuickScrub = false;
- if (ENABLE_AUTO_ADVANCE) {
- mAutoAdvanceAlarm.cancelAlarm();
- }
- int page = mRecentsView.getNextPage();
+
Runnable launchTaskRunnable = () -> {
+ int page = mRecentsView.getPageNearestToCenterOfScreen();
TaskView taskView = mRecentsView.getTaskViewAt(page);
if (taskView != null) {
mWaitingForTaskLaunch = true;
@@ -118,12 +159,49 @@
TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key));
}
mWaitingForTaskLaunch = false;
+ if (mIsQuickSwitch) {
+ mIsQuickSwitch = false;
+ if (mRecentsView.getTaskViewCount() > 0) {
+ mRecentsView.getTaskViewAt(0).setFullscreen(false);
+ }
+ if (mRecentsView.getTaskViewCount() > 1) {
+ mRecentsView.getTaskViewAt(1).setFullscreen(false);
+ }
+ }
+
}, taskView.getHandler());
} else {
breakOutOfQuickScrub();
}
mActivityControlHelper = null;
};
+
+ if (mIsQuickSwitch) {
+ float progressVelocity = mPrevPrevProgressDelta / SINGLE_FRAME_MS;
+ // Move to the next frame immediately, then start the animation from the
+ // following frame since it starts a frame later.
+ float singleFrameProgress = progressVelocity * SINGLE_FRAME_MS;
+ float fromProgress = mEndProgress + singleFrameProgress;
+ onQuickScrubProgress(fromProgress);
+ fromProgress += singleFrameProgress;
+ float toProgress = mShouldSwitchToNext ? 1 : 0;
+ int duration = (int) Math.abs((toProgress - fromProgress) / progressVelocity);
+ duration = Utilities.boundToRange(duration, 80, 300);
+ Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, fromProgress, toProgress);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ launchTaskRunnable.run();
+ }
+ });
+ anim.setDuration(duration).start();
+ return;
+ }
+
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm.cancelAlarm();
+ }
+ int page = mRecentsView.getNextPage();
int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
* QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
if (mRecentsView.getChildCount() > 0 && mRecentsView.snapToPage(page, snapDuration)) {
@@ -151,19 +229,28 @@
mLaunchingTaskId = 0;
}
+ public boolean prepareQuickScrub(String tag) {
+ return prepareQuickScrub(tag, mIsQuickSwitch);
+ }
+
/**
* Initializes the UI for quick scrub, returns true if success.
*/
- public boolean prepareQuickScrub(String tag) {
+ public boolean prepareQuickScrub(String tag, boolean isQuickSwitch) {
if (mWaitingForTaskLaunch || mInQuickScrub) {
Log.d(tag, "Waiting for last scrub to finish, will skip this interaction");
return false;
}
mOnFinishedTransitionToQuickScrubRunnable = null;
mRecentsView.setNextPageSwitchRunnable(null);
+ mIsQuickSwitch = isQuickSwitch;
return true;
}
+ public boolean isQuickSwitch() {
+ return mIsQuickSwitch;
+ }
+
public boolean isWaitingForTaskLaunch() {
return mWaitingForTaskLaunch;
}
@@ -179,6 +266,40 @@
}
public void onQuickScrubProgress(float progress) {
+ if (mIsQuickSwitch) {
+ TaskView currentPage = mRecentsView.getTaskViewAt(0);
+ TaskView nextPage = mRecentsView.getTaskViewAt(1);
+ if (currentPage == null || nextPage == null) {
+ return;
+ }
+ if (!mFinishedTransitionToQuickScrub) {
+ mStartProgress = mEndProgress = progress;
+ } else {
+ float progressDelta = progress - mEndProgress;
+ mEndProgress = progress;
+ progress = Utilities.boundToRange(progress, mStartProgress, 1);
+ progress = Utilities.mapToRange(progress, mStartProgress, 1, 0, 1, LINEAR);
+ if (mInQuickScrub) {
+ mShouldSwitchToNext = mPrevProgressDelta > 0.007f || progressDelta > 0.007f
+ || progress >= 0.5f;
+ }
+ mPrevPrevProgressDelta = mPrevProgressDelta;
+ mPrevProgressDelta = progressDelta;
+ float scrollDiff = nextPage.getWidth() + mRecentsView.getPageSpacing();
+ int scrollDir = mRecentsView.isRtl() ? -1 : 1;
+ int linearScrollDiff = (int) (progress * scrollDiff * scrollDir);
+ float accelScrollDiff = ACCEL.getInterpolation(progress) * scrollDiff * scrollDir;
+ currentPage.setZoomScale(1 - DEACCEL_3.getInterpolation(progress)
+ * TaskView.EDGE_SCALE_DOWN_FACTOR);
+ currentPage.setTranslationX(linearScrollDiff + accelScrollDiff);
+ nextPage.setTranslationZ(1);
+ nextPage.setTranslationY(currentPage.getTranslationY());
+ int startScroll = mRecentsView.isRtl() ? mRecentsView.getMaxScrollX() : 0;
+ mRecentsView.setScrollX(startScroll + linearScrollDiff);
+ }
+ return;
+ }
+
int quickScrubSection = 0;
for (float threshold : QUICK_SCRUB_THRESHOLDS) {
if (progress < threshold) {
@@ -228,9 +349,14 @@
public void snapToNextTaskIfAvailable() {
if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
- int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
- : QUICK_SCRUB_FROM_APP_START_DURATION;
- int pageToGoTo = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
+ int duration = mIsQuickSwitch
+ ? QUICK_SWITCH_FROM_APP_START_DURATION
+ : mStartedFromHome
+ ? QUICK_SCRUB_FROM_HOME_START_DURATION
+ : QUICK_SCRUB_FROM_APP_START_DURATION;
+ int pageToGoTo = mStartedFromHome || mIsQuickSwitch
+ ? 0
+ : mRecentsView.getNextPage() + 1;
goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
QUICK_SCRUB_START_INTERPOLATOR);
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 9ea8884..a604da0 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
+import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -59,6 +60,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -979,12 +981,14 @@
setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
// Start the window animation without waiting for launcher.
- animateToProgress(mCurrentShift.value, 1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR,
- true /* goingToHome */);
+ long duration = FeatureFlags.QUICK_SWITCH.get()
+ ? QUICK_SWITCH_FROM_APP_START_DURATION
+ : QUICK_SCRUB_FROM_APP_START_DURATION;
+ animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */);
}
private void onQuickScrubStartUi() {
- if (!mQuickScrubController.prepareQuickScrub(TAG)) {
+ if (!mQuickScrubController.prepareQuickScrub(TAG, FeatureFlags.QUICK_SWITCH.get())) {
mQuickScrubBlocked = true;
setStateOnUiThread(STATE_RESUME_LAST_TASK | STATE_HANDLER_INVALIDATED);
return;
@@ -993,6 +997,7 @@
mLauncherTransitionController.getAnimationPlayer().end();
mLauncherTransitionController = null;
}
+ mLayoutListener.finish();
mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false,
mTouchInteractionLog);
@@ -1008,6 +1013,13 @@
mQuickScrubController.onFinishedTransitionToQuickScrub();
mRecentsView.animateUpRunningTaskIconScale();
+ if (mQuickScrubController.isQuickSwitch()) {
+ TaskView runningTask = mRecentsView.getRunningTaskView();
+ if (runningTask != null) {
+ runningTask.setTranslationY(-mActivity.getResources().getDimension(
+ R.dimen.task_thumbnail_half_top_margin) * 1f / mRecentsView.getScaleX());
+ }
+ }
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index ce65de1..c92c8d6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -17,7 +17,6 @@
package com.android.quickstep.views;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
-
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -33,7 +32,7 @@
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
-
+import android.view.ViewGroup;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -78,6 +77,7 @@
private final Matrix mMatrix = new Matrix();
private float mClipBottom = -1;
+ private Rect mScaledInsets = new Rect();
private Task mTask;
private ThumbnailData mThumbnailData;
@@ -179,7 +179,17 @@
@Override
protected void onDraw(Canvas canvas) {
- drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
+ if (((TaskView) getParent()).isFullscreen()) {
+ // Draw the insets if we're being drawn fullscreen (we do this for quick switch).
+ drawOnCanvas(canvas,
+ -mScaledInsets.left,
+ -mScaledInsets.top,
+ getMeasuredWidth() + mScaledInsets.right,
+ getMeasuredHeight() + mScaledInsets.bottom,
+ mCornerRadius);
+ } else {
+ drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
+ }
}
public float getCornerRadius() {
@@ -253,6 +263,9 @@
: getMeasuredWidth() / thumbnailWidth;
}
+ mScaledInsets.set(thumbnailInsets);
+ Utilities.scaleRect(mScaledInsets, thumbnailScale);
+
if (rotate) {
int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
mMatrix.setRotate(90 * rotationDir);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d34dc5b..1e787a2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -87,7 +87,7 @@
/**
* How much to scale down pages near the edge of the screen.
*/
- private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
+ public static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
@@ -142,6 +142,7 @@
private IconView mIconView;
private float mCurveScale;
private float mZoomScale;
+ private boolean mIsFullscreen;
private Animator mIconAndDimAnimator;
private float mFocusTransitionProgress = 1;
@@ -512,4 +513,18 @@
Log.w(tag, msg);
Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
}
+
+ /**
+ * Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
+ */
+ public void setFullscreen(boolean isFullscreen) {
+ mIsFullscreen = isFullscreen;
+ mIconView.setVisibility(mIsFullscreen ? INVISIBLE : VISIBLE);
+ setClipChildren(!mIsFullscreen);
+ setClipToPadding(!mIsFullscreen);
+ }
+
+ public boolean isFullscreen() {
+ return mIsFullscreen;
+ }
}
diff --git a/res/values/colors.xml b/res/values/colors.xml
index eb207af..3c8fe1e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -34,7 +34,6 @@
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
- <color name="legacy_icon_background">#FFFFFF</color>
<color name="all_apps_bg_hand_fill">#E5E5E5</color>
<color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
new file mode 100644
index 0000000..5011764
--- /dev/null
+++ b/robolectric_tests/Android.mk
@@ -0,0 +1,53 @@
+# 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.
+
+#############################################
+# Launcher Robolectric test target. #
+#############################################
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := LauncherRoboTests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.test.runner \
+ androidx.test.rules \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt
+LOCAL_JAVA_LIBRARIES := \
+ platform-robolectric-3.6.1-prebuilt
+
+LOCAL_INSTRUMENTATION_FOR := Launcher3
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+############################################
+# Target to run the previous target. #
+############################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunLauncherRoboTests
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES := \
+ LauncherRoboTests
+
+LOCAL_TEST_PACKAGE := Launcher3
+
+LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
+
+LOCAL_ROBOTEST_TIMEOUT := 36000
+
+include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
similarity index 89%
rename from tests/src/com/android/launcher3/logging/FileLogTest.java
rename to robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index e031f1d..096db57 100644
--- a/tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -1,13 +1,11 @@
package com.android.launcher3.logging;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
import java.io.File;
import java.io.PrintWriter;
@@ -20,8 +18,7 @@
/**
* Tests for {@link FileLog}
*/
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
public class FileLogTest {
private File mTempDir;
@@ -30,9 +27,9 @@
public void setUp() throws Exception {
int count = 0;
do {
- mTempDir = new File(InstrumentationRegistry.getTargetContext().getCacheDir(),
+ mTempDir = new File(RuntimeEnvironment.application.getCacheDir(),
"log-test-" + (count++));
- } while(!mTempDir.mkdir());
+ } while (!mTempDir.mkdir());
FileLog.setDir(mTempDir);
}
diff --git a/tests/src/com/android/launcher3/util/GridOccupancyTest.java b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
similarity index 92%
rename from tests/src/com/android/launcher3/util/GridOccupancyTest.java
rename to robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
index cbf30b1..aa51ad2 100644
--- a/tests/src/com/android/launcher3/util/GridOccupancyTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -1,10 +1,8 @@
package com.android.launcher3.util;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -13,8 +11,7 @@
/**
* Unit tests for {@link GridOccupancy}
*/
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
public class GridOccupancyTest {
@Test
@@ -24,7 +21,7 @@
0, 0, 1, 1, 0,
0, 0, 0, 0, 0,
1, 1, 0, 0, 0
- );
+ );
int[] vacant = new int[2];
assertTrue(grid.findVacantCell(vacant, 2, 2));
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
new file mode 100644
index 0000000..faec380
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.launcher3.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Robolectric unit tests for {@link IntSet}
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 26)
+public class IntSetTest {
+
+ @Test
+ public void shouldBeEmptyInitially() {
+ IntSet set = new IntSet();
+ assertThat(set.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void oneElementSet() {
+ IntSet set = new IntSet();
+ set.add(2);
+ assertThat(set.size()).isEqualTo(1);
+ assertTrue(set.contains(2));
+ assertFalse(set.contains(1));
+ }
+
+
+ @Test
+ public void twoElementSet() {
+ IntSet set = new IntSet();
+ set.add(2);
+ set.add(1);
+ assertThat(set.size()).isEqualTo(2);
+ assertTrue(set.contains(2));
+ assertTrue(set.contains(1));
+ }
+
+ @Test
+ public void threeElementSet() {
+ IntSet set = new IntSet();
+ set.add(2);
+ set.add(1);
+ set.add(10);
+ assertThat(set.size()).isEqualTo(3);
+ assertEquals("1, 2, 10", set.mArray.toConcatString());
+ }
+
+
+ @Test
+ public void duplicateEntries() {
+ IntSet set = new IntSet();
+ set.add(2);
+ set.add(2);
+ assertEquals(1, set.size());
+ }
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..b52bd4f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':IconLoader'
+project(':IconLoader').projectDir = new File(rootDir, 'iconloaderlib')
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 36b9e97..9470635 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -625,6 +625,10 @@
mMaxScrollX = computeMaxScrollX();
}
+ public int getMaxScrollX() {
+ return mMaxScrollX;
+ }
+
protected int computeMaxScrollX() {
int childCount = getChildCount();
if (childCount > 0) {
@@ -640,6 +644,10 @@
requestLayout();
}
+ public int getPageSpacing() {
+ return mPageSpacing;
+ }
+
private void dispatchPageCountChanged() {
if (mPageIndicator != null) {
mPageIndicator.setMarkersCount(getChildCount());
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 64b5652..e5a8a01 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -22,9 +22,6 @@
import android.content.SharedPreferences;
import android.provider.Settings;
-import androidx.annotation.GuardedBy;
-import androidx.annotation.Keep;
-
import com.android.launcher3.Utilities;
import java.util.ArrayList;
@@ -32,6 +29,9 @@
import java.util.SortedMap;
import java.util.TreeMap;
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Keep;
+
/**
* Defines a set of flags used to control various launcher behaviors.
*
@@ -87,6 +87,9 @@
// trying to make them fit the orientation the device is in.
public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
+ public static final TogglableFlag QUICK_SWITCH = new TogglableFlag("QUICK_SWITCH", false,
+ "Swiping right on the nav bar while in an app switches to the previous app");
+
/**
* Feature flag to handle define config changes dynamically instead of killing the process.
*/
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 00cc1a7..6c98d16 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -121,7 +121,6 @@
private Workspace mWorkspace;
- private final boolean mHasSysUiScrim;
private boolean mDrawTopScrim, mDrawBottomScrim;
private final RectF mFinalMaskRect = new RectF();
@@ -149,15 +148,9 @@
mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_BITMAP_DP,
view.getResources().getDisplayMetrics());
-
- mHasSysUiScrim = !mWallpaperColorInfo.supportsDarkText();
- if (mHasSysUiScrim) {
- mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
- mBottomMask = createDitheredAlphaMask();
- } else {
- mTopScrim = null;
- mBottomMask = null;
- }
+ mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
+ mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask();
+ mHideSysUiScrim = mTopScrim == null;
view.addOnAttachStateChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
@@ -185,7 +178,7 @@
canvas.restore();
}
- if (!mHideSysUiScrim && mHasSysUiScrim) {
+ if (!mHideSysUiScrim) {
if (mSysUiProgress <= 0) {
mAnimateScrimOnNextDraw = false;
return;
@@ -213,8 +206,9 @@
}
public void onInsetsChanged(Rect insets) {
- mDrawTopScrim = insets.top > 0;
- mDrawBottomScrim = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+ mDrawTopScrim = mTopScrim != null && insets.top > 0;
+ mDrawBottomScrim = mBottomMask != null &&
+ !mLauncher.getDeviceProfile().isVerticalBarLayout();
}
private void setScrimProgress(float progress) {
@@ -230,7 +224,7 @@
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
- if (mHasSysUiScrim) {
+ if (mTopScrim != null) {
IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
mRoot.getContext().registerReceiver(mReceiver, filter);
@@ -240,7 +234,7 @@
@Override
public void onViewDetachedFromWindow(View view) {
mWallpaperColorInfo.removeOnChangeListener(this);
- if (mHasSysUiScrim) {
+ if (mTopScrim != null) {
mRoot.getContext().unregisterReceiver(mReceiver);
}
}
@@ -259,14 +253,14 @@
}
public void setSize(int w, int h) {
- if (mHasSysUiScrim) {
+ if (mTopScrim != null) {
mTopScrim.setBounds(0, 0, w, h);
mFinalMaskRect.set(0, h - mMaskHeight, w, h);
}
}
public void hideSysUiScrim(boolean hideSysUiScrim) {
- mHideSysUiScrim = hideSysUiScrim;
+ mHideSysUiScrim = hideSysUiScrim || (mTopScrim == null);
if (!hideSysUiScrim) {
mAnimateScrimOnNextDraw = true;
}
@@ -281,18 +275,18 @@
}
private void reapplySysUiAlpha() {
- if (mHasSysUiScrim) {
- reapplySysUiAlphaNoInvalidate();
- if (!mHideSysUiScrim) {
- invalidate();
- }
+ reapplySysUiAlphaNoInvalidate();
+ if (!mHideSysUiScrim) {
+ invalidate();
}
}
private void reapplySysUiAlphaNoInvalidate() {
float factor = mSysUiProgress * mSysUiAnimMultiplier;
mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor));
- mTopScrim.setAlpha(Math.round(255 * factor));
+ if (mTopScrim != null) {
+ mTopScrim.setAlpha(Math.round(255 * factor));
+ }
}
public void invalidate() {
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
similarity index 100%
rename from src/com/android/launcher3/model/WidgetsModel.java
rename to src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
similarity index 100%
rename from src/com/android/launcher3/shortcuts/DeepShortcutManager.java
rename to src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 439058c..46b463b 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -18,6 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3.tests">
+ <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
+
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/tests/dummy_app/Android.mk b/tests/dummy_app/Android.mk
new file mode 100644
index 0000000..f4ab582
--- /dev/null
+++ b/tests/dummy_app/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Aardwolf
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
new file mode 100644
index 0000000..0546015
--- /dev/null
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<!-- Declare the contents of this Android application. The namespace
+ attribute brings in the Android platform namespace, and the package
+ supplies a unique name for the application. When writing your
+ own application, the package name must be changed from "com.example.*"
+ to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.aardwolf">
+ <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="21"/>
+ <application android:label="Aardwolf">
+ <activity
+ android:name="Activity1"
+ android:icon="@mipmap/ic_launcher1"
+ android:label="Aardwolf">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/dummy_app/res/layout/empty_activity.xml b/tests/dummy_app/res/layout/empty_activity.xml
new file mode 100644
index 0000000..377c56b
--- /dev/null
+++ b/tests/dummy_app/res/layout/empty_activity.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<EditText xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textSize="18sp"
+ android:autoText="true"
+ android:capitalize="sentences"
+ android:text="Did you enjoy the adaptive icon?" />
+
diff --git a/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml b/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml
new file mode 100644
index 0000000..37c8c73
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@mipmap/icon_back_1"/>
+ <foreground>
+ <bitmap android:src="@mipmap/icon_fore_1"/>
+ </foreground>
+</adaptive-icon>
diff --git a/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml b/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml
new file mode 100644
index 0000000..20b2986
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/background" />
+ <foreground android:drawable="@mipmap/icon_fore_1"/>
+</adaptive-icon>
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png
new file mode 100644
index 0000000..73bc8e6
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..cd40b63
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png b/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png
new file mode 100644
index 0000000..8debef3
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png b/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png
new file mode 100644
index 0000000..de4079b
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png
new file mode 100644
index 0000000..889a99c
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..973bb79
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png b/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png
new file mode 100644
index 0000000..70c0ebd
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png b/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png
new file mode 100644
index 0000000..9d91632
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png
Binary files differ
diff --git a/tests/dummy_app/res/values/colors.xml b/tests/dummy_app/res/values/colors.xml
new file mode 100644
index 0000000..b5ce66e
--- /dev/null
+++ b/tests/dummy_app/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="background">#455A64</color>
+</resources>
\ No newline at end of file
diff --git a/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java b/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java
new file mode 100644
index 0000000..d4eab15
--- /dev/null
+++ b/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.example.android.aardwolf;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+public class Activity1 extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.empty_activity, null);
+ setContentView(view);
+ }
+}
+
diff --git a/tests/res/raw/aardwolf_dummy_app.apk b/tests/res/raw/aardwolf_dummy_app.apk
new file mode 100644
index 0000000..39fb368
--- /dev/null
+++ b/tests/res/raw/aardwolf_dummy_app.apk
Binary files differ
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
index 04c04f5..0edb3d6 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -19,6 +19,8 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DONT_KILL_APP;
+import android.app.Activity;
+import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -36,6 +38,7 @@
public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher";
public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher";
+ public static final String KILL_PROCESS = "kill-process";
@Override
public boolean onCreate() {
@@ -83,14 +86,22 @@
COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
return null;
}
-
+ case KILL_PROCESS: {
+ ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)).
+ killBackgroundProcesses(arg);
+ return null;
+ }
}
return super.call(method, arg, extras);
}
public static Bundle callCommand(String command) {
+ return callCommand(command, null);
+ }
+
+ public static Bundle callCommand(String command, String arg) {
Instrumentation inst = InstrumentationRegistry.getInstrumentation();
Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
- return inst.getTargetContext().getContentResolver().call(uri, command, null, null);
+ return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 532d3e8..bc5aaee 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -296,11 +296,6 @@
Process.myUserHandle()).get(0);
}
- protected LauncherActivityInfo getChromeApp() {
- return LauncherAppsCompat.getInstance(mTargetContext)
- .getActivityList("com.android.chrome", Process.myUserHandle()).get(0);
- }
-
/**
* Broadcast receiver which blocks until the result is received.
*/
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 9160076..9354862 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -1,18 +1,10 @@
package com.android.launcher3.ui;
-import static org.junit.Assert.assertTrue;
-
import android.content.pm.LauncherActivityInfo;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,37 +16,23 @@
public class AllAppsIconToHomeTest extends AbstractLauncherUiTest {
@Test
- @Ignore
- public void testDragIcon_portrait() throws Throwable {
- lockRotation(true);
- performTest();
- }
-
- @Test
- @Ignore
- public void testDragIcon_landscape() throws Throwable {
- lockRotation(false);
- performTest();
- }
-
- private void performTest() throws Throwable {
+ @PortraitLandscape
+ public void testDragIcon() throws Throwable {
LauncherActivityInfo settingsApp = getSettingsApp();
clearHomescreen();
mDevice.pressHome();
mDevice.waitForIdle();
- // Open all apps and wait for load complete.
- final UiObject2 appsContainer = TestViewHelpers.openAllApps();
- Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
-
- // Drag icon to homescreen.
- UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
- TestViewHelpers.dragToWorkspace(icon, true);
-
- // Verify that the icon works on homescreen.
- mDevice.findObject(By.text(settingsApp.getLabel().toString())).click();
- assertTrue(mDevice.wait(Until.hasObject(By.pkg(
- settingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+ final String appName = settingsApp.getLabel().toString();
+ // 1. Open all apps and wait for load complete.
+ // 2. Drag icon to homescreen.
+ // 3. Verify that the icon works on homescreen.
+ mLauncher.getWorkspace().
+ switchToAllApps().
+ getAppIcon(appName).
+ dragToWorkspace().
+ getWorkspaceAppIcon(appName).
+ launch(settingsApp.getComponentName().getPackageName());
}
}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
index d7a7f6b..1fea4d5 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -1,22 +1,18 @@
package com.android.launcher3.ui;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.pm.LauncherActivityInfo;
-import android.graphics.Point;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-import android.view.MotionEvent;
-import com.android.launcher3.R;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.popup.ArrowPopup;
+import com.android.launcher3.tapl.AppIconMenu;
+import com.android.launcher3.tapl.AppIconMenuItem;
+import com.android.launcher3.views.OptionsPopupView;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,47 +23,30 @@
@RunWith(AndroidJUnit4.class)
public class ShortcutsLaunchTest extends AbstractLauncherUiTest {
- @Test
- @Ignore
- public void testAppLauncher_portrait() throws Exception {
- lockRotation(true);
- performTest();
+ private boolean isOptionsPopupVisible(Launcher launcher) {
+ final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
+ return popup != null && popup.isShown();
}
@Test
- @Ignore
- public void testAppLauncher_landscape() throws Exception {
- lockRotation(false);
- performTest();
- }
-
- private void performTest() throws Exception {
+ @PortraitLandscape
+ public void testAppLauncher() throws Exception {
mActivityMonitor.startLauncher();
- LauncherActivityInfo testApp = getSettingsApp();
+ final LauncherActivityInfo testApp = getSettingsApp();
- // Open all apps and wait for load complete
- final UiObject2 appsContainer = TestViewHelpers.openAllApps();
- Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
+ final AppIconMenu menu = mLauncher.
+ pressHome().
+ switchToAllApps().
+ getAppIcon(testApp.getLabel().toString()).
+ openMenu();
- // Find settings app and verify shortcuts appear when long pressed
- UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
- // Press icon center until shortcuts appear
- Point iconCenter = icon.getVisibleCenter();
- TestViewHelpers.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
- UiObject2 deepShortcutsContainer = TestViewHelpers.findViewById(
- R.id.deep_shortcuts_container);
- assertNotNull(deepShortcutsContainer);
- TestViewHelpers.sendPointer(MotionEvent.ACTION_UP, iconCenter);
+ executeOnLauncher(
+ launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+ isOptionsPopupVisible(launcher)));
- // Verify that launching a shortcut opens a page with the same text
- assertTrue(deepShortcutsContainer.getChildCount() > 0);
+ final AppIconMenuItem menuItem = menu.getMenuItem(1);
+ final String itemName = menuItem.getText();
- // Pick second children as it starts showing shortcuts.
- UiObject2 shortcut = deepShortcutsContainer.getChildren().get(1)
- .findObject(TestViewHelpers.getSelectorForId(R.id.bubble_text));
- shortcut.click();
- assertTrue(mDevice.wait(Until.hasObject(By.pkg(
- testApp.getComponentName().getPackageName())
- .text(shortcut.getText())), DEFAULT_UI_TIMEOUT));
+ menuItem.launch(testApp.getComponentName().getPackageName(), itemName);
}
}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
index 436c699..4c2c959 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -1,22 +1,12 @@
package com.android.launcher3.ui;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
import android.content.pm.LauncherActivityInfo;
-import android.graphics.Point;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-import android.view.MotionEvent;
-import com.android.launcher3.R;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
+import com.android.launcher3.tapl.AppIconMenuItem;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -28,51 +18,30 @@
public class ShortcutsToHomeTest extends AbstractLauncherUiTest {
@Test
- @Ignore
- public void testDragIcon_portrait() throws Throwable {
- lockRotation(true);
- performTest();
- }
-
- @Test
- @Ignore
- public void testDragIcon_landscape() throws Throwable {
- lockRotation(false);
- performTest();
- }
-
- private void performTest() throws Throwable {
+ @PortraitLandscape
+ public void testDragIcon() throws Throwable {
clearHomescreen();
mActivityMonitor.startLauncher();
- LauncherActivityInfo testApp = getSettingsApp();
+ LauncherActivityInfo testApp = getSettingsApp();
- // Open all apps and wait for load complete.
- final UiObject2 appsContainer = TestViewHelpers.openAllApps();
- Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
+ // 1. Open all apps and wait for load complete.
+ // 2. Find the app and long press it to show shortcuts.
+ // 3. Press icon center until shortcuts appear
+ final AppIconMenuItem menuItem = mLauncher.
+ getWorkspace().
+ switchToAllApps().
+ getAppIcon(testApp.getLabel().toString()).
+ openMenu().
+ getMenuItem(0);
+ final String shortcutName = menuItem.getText();
- // Find the app and long press it to show shortcuts.
- UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
- // Press icon center until shortcuts appear
- Point iconCenter = icon.getVisibleCenter();
- TestViewHelpers.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
- UiObject2 deepShortcutsContainer = TestViewHelpers.findViewById(
- R.id.deep_shortcuts_container);
- assertNotNull(deepShortcutsContainer);
- TestViewHelpers.sendPointer(MotionEvent.ACTION_UP, iconCenter);
-
- // Drag the first shortcut to the home screen.
- assertTrue(deepShortcutsContainer.getChildCount() > 0);
- UiObject2 shortcut = deepShortcutsContainer.getChildren().get(1)
- .findObject(TestViewHelpers.getSelectorForId(R.id.bubble_text));
- String shortcutName = shortcut.getText();
- TestViewHelpers.dragToWorkspace(shortcut, false);
-
- // Verify that the shortcut works on home screen
- // (the app opens and has the same text as the shortcut).
- mDevice.findObject(By.text(shortcutName)).click();
- assertTrue(mDevice.wait(Until.hasObject(By.pkg(
- testApp.getComponentName().getPackageName())
- .text(shortcutName)), DEFAULT_UI_TIMEOUT));
+ // 4. Drag the first shortcut to the home screen.
+ // 5. Verify that the shortcut works on home screen
+ // (the app opens and has the same text as the shortcut).
+ menuItem.
+ dragToWorkspace().
+ getWorkspaceAppIcon(shortcutName).
+ launch(testApp.getComponentName().getPackageName(), shortcutName);
}
}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
new file mode 100644
index 0000000..1338dcb
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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.launcher3.util;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.content.res.Resources;
+
+import androidx.test.uiautomator.UiDevice;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class TestUtil {
+ public static void installDummyApp() throws IOException {
+ // Copy apk from resources to a local file and install from there.
+ final Resources resources = getContext().getResources();
+ final InputStream in = resources.openRawResource(
+ resources.getIdentifier("aardwolf_dummy_app",
+ "raw", getContext().getPackageName()));
+ final String apkFilename = getInstrumentation().getTargetContext().
+ getFilesDir().getPath() + "/dummy_app.apk";
+
+ final FileOutputStream out = new FileOutputStream(apkFilename);
+ byte[] buff = new byte[1024];
+ int read;
+
+ while ((read = in.read(buff)) > 0) {
+ out.write(buff, 0, read);
+ }
+ in.close();
+ out.close();
+
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand("pm install " + apkFilename);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index b7ae9f1..efefc0d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -16,23 +16,19 @@
package com.android.launcher3.tapl;
+import android.graphics.Point;
import android.widget.TextView;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
/**
* App icon, whether in all apps or in workspace/
*/
-public final class AppIcon {
- private final LauncherInstrumentation mLauncher;
- private final UiObject2 mIcon;
-
+public final class AppIcon extends Launchable {
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
- mLauncher = launcher;
- mIcon = icon;
+ super(launcher, icon);
}
static BySelector getAppIconSelector(String appName) {
@@ -40,20 +36,13 @@
}
/**
- * Clicks the icon to launch its app.
+ * Long-clicks the icon to open its menu.
*/
- public Background launch(String packageName) {
- LauncherInstrumentation.log("AppIcon.launch before click " + mIcon.getVisibleCenter());
- LauncherInstrumentation.assertTrue(
- "Launching an app didn't open a new window: " + mIcon.getText(),
- mIcon.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
- LauncherInstrumentation.assertTrue(
- "App didn't start: " + packageName, mLauncher.getDevice().wait(Until.hasObject(
- By.pkg(packageName).depth(0)), LauncherInstrumentation.WAIT_TIME_MS));
- return new Background(mLauncher);
- }
-
- UiObject2 getIcon() {
- return mIcon;
+ public AppIconMenu openMenu() {
+ final Point iconCenter = mObject.getVisibleCenter();
+ mLauncher.longTap(iconCenter.x, iconCenter.y);
+ final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
+ "deep_shortcuts_container");
+ return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
new file mode 100644
index 0000000..2a03f9a
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -0,0 +1,46 @@
+/*
+ * 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.launcher3.tapl;
+
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Context menu of an app icon.
+ */
+public class AppIconMenu {
+ private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mDeepShortcutsContainer;
+
+ AppIconMenu(LauncherInstrumentation launcher,
+ UiObject2 deepShortcutsContainer) {
+ mLauncher = launcher;
+ mDeepShortcutsContainer = deepShortcutsContainer;
+ }
+
+ /**
+ * Returns a menu item with a given number. Fails if it doesn't exist.
+ */
+ public AppIconMenuItem getMenuItem(int itemNumber) {
+ assertTrue(mDeepShortcutsContainer.getChildCount() > itemNumber);
+
+ final UiObject2 shortcut = mLauncher.waitForObjectInContainer(
+ mDeepShortcutsContainer.getChildren().get(itemNumber), "bubble_text");
+ return new AppIconMenuItem(mLauncher, shortcut);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
new file mode 100644
index 0000000..c39f8d1
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -0,0 +1,35 @@
+/*
+ * 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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Menu item in an app icon menu.
+ */
+public class AppIconMenuItem extends Launchable {
+ AppIconMenuItem(LauncherInstrumentation launcher, UiObject2 shortcut) {
+ super(launcher, shortcut);
+ }
+
+ /**
+ * Returns the visible text of the menu item.
+ */
+ public String getText() {
+ return mObject.getText();
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
new file mode 100644
index 0000000..7e2c966
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -0,0 +1,81 @@
+/*
+ * 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.launcher3.tapl;
+
+import android.graphics.Point;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+/**
+ * Ancestor for AppIcon and AppMenuItem.
+ */
+class Launchable {
+ private static final int DRAG_SPEED = 500;
+ protected final LauncherInstrumentation mLauncher;
+
+ protected final UiObject2 mObject;
+
+ Launchable(LauncherInstrumentation launcher, UiObject2 object) {
+ mObject = object;
+ mLauncher = launcher;
+ }
+
+ UiObject2 getObject() {
+ return mObject;
+ }
+
+ /**
+ * Clicks the object to launch its app.
+ */
+ public Background launch(String expectedPackageName) {
+ return launch(expectedPackageName, By.pkg(expectedPackageName).depth(0));
+ }
+
+ /**
+ * Clicks the object to launch its app.
+ */
+ public Background launch(String expectedPackageName, String expectedAppText) {
+ return launch(expectedPackageName, By.pkg(expectedPackageName).text(expectedAppText));
+ }
+
+ private Background launch(String errorMessage, BySelector selector) {
+ LauncherInstrumentation.log("Launchable.launch before click " +
+ mObject.getVisibleCenter());
+ LauncherInstrumentation.assertTrue(
+ "Launching an app didn't open a new window: " + mObject.getText(),
+ mObject.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
+ LauncherInstrumentation.assertTrue(
+ "App didn't start: " + errorMessage,
+ mLauncher.getDevice().wait(Until.hasObject(selector),
+ LauncherInstrumentation.WAIT_TIME_MS));
+ return new Background(mLauncher);
+ }
+
+ /**
+ * Drags an object to the center of homescreen.
+ */
+ public Workspace dragToWorkspace() {
+ final UiDevice device = mLauncher.getDevice();
+ mObject.drag(new Point(
+ device.getDisplayWidth() / 2, device.getDisplayHeight() / 2), DRAG_SPEED);
+ return new Workspace(mLauncher);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 31abc53..67106f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -28,6 +28,13 @@
import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
import com.android.launcher3.TestProtocol;
import com.android.quickstep.SwipeUpSetting;
@@ -36,13 +43,6 @@
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeoutException;
-import androidx.annotation.NonNull;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
* produces all other objects.
@@ -394,6 +394,11 @@
return mDevice;
}
+ void longTap(int x, int y) {
+ mDevice.drag(x, y, x, y, 0);
+ }
+
+
void swipe(int startX, int startY, int endX, int endY) {
executeAndWaitForEvent(
() -> mDevice.swipe(startX, startY, endX, endY, 60),
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 493f26a..c63822e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -19,12 +19,12 @@
import static junit.framework.TestCase.assertTrue;
import android.graphics.Point;
-import androidx.test.uiautomator.Direction;
-import androidx.test.uiautomator.UiObject2;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiObject2;
/**
* Operations on the workspace screen.
@@ -85,6 +85,21 @@
return icon != null ? new AppIcon(mLauncher, icon) : null;
}
+
+ /**
+ * Returns an icon for the app; fails if the icon doesn't exist.
+ *
+ * @param appName name of the app
+ * @return app icon.
+ */
+ @NonNull
+ public AppIcon getWorkspaceAppIcon(String appName) {
+ return new AppIcon(mLauncher,
+ mLauncher.getObjectInContainer(
+ verifyActiveContainer(),
+ AppIcon.getAppIconSelector(appName)));
+ }
+
/**
* Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
* second screen.
@@ -111,7 +126,7 @@
private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) {
final Point dest = new Point(
mLauncher.getDevice().getDisplayWidth(), workspace.getVisibleBounds().centerY());
- app.getIcon().drag(dest, ICON_DRAG_SPEED);
+ app.getObject().drag(dest, ICON_DRAG_SPEED);
verifyActiveContainer();
}