Merging from ub-launcher3-rvc-qpr-dev @ build 6690853

Bug:150504032
Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-rvc-qpr-dev-rvc-qpr-dev_6690853.html

Change-Id: Ie86b0a2e0d82646a0c3d44a0214cbb985435320d
Merged-In: Ibff46b3ef7ff89accb459db323f31179adb4ef21
This commit is contained in:
Hyunyoung Song 2020-07-19 20:29:17 -07:00
commit 65b16d6db2
102 changed files with 1499 additions and 1229 deletions

View File

@ -45,9 +45,6 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- TODO(b/150802536): Enabled only for ENABLE_FIXED_ROTATION_TRANSFORM feature flag -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<!-- <!--
Permissions required for read/write access to the workspace data. These permission name Permissions required for read/write access to the workspace data. These permission name
@ -86,6 +83,7 @@
<receiver <receiver
android:name="com.android.launcher3.InstallShortcutReceiver" android:name="com.android.launcher3.InstallShortcutReceiver"
android:permission="com.android.launcher.permission.INSTALL_SHORTCUT" android:permission="com.android.launcher.permission.INSTALL_SHORTCUT"
android:exported="true"
android:enabled="@bool/enable_install_shortcut_api" > android:enabled="@bool/enable_install_shortcut_api" >
<intent-filter> <intent-filter>
<action android:name="com.android.launcher.action.INSTALL_SHORTCUT" /> <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
@ -94,14 +92,16 @@
<!-- Intent received when a session is committed --> <!-- Intent received when a session is committed -->
<receiver <receiver
android:name="com.android.launcher3.SessionCommitReceiver" > android:name="com.android.launcher3.SessionCommitReceiver"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.content.pm.action.SESSION_COMMITTED" /> <action android:name="android.content.pm.action.SESSION_COMMITTED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Intent received used to initialize a restored widget --> <!-- Intent received used to initialize a restored widget -->
<receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" > <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/> <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
</intent-filter> </intent-filter>
@ -117,6 +117,7 @@
android:name="com.android.launcher3.notification.NotificationListener" android:name="com.android.launcher3.notification.NotificationListener"
android:label="@string/notification_dots_service_title" android:label="@string/notification_dots_service_title"
android:enabled="@bool/notification_dots_enabled" android:enabled="@bool/notification_dots_enabled"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter> <intent-filter>
<action android:name="android.service.notification.NotificationListenerService" /> <action android:name="android.service.notification.NotificationListenerService" />
@ -130,6 +131,7 @@
android:theme="@style/AppItemActivityTheme" android:theme="@style/AppItemActivityTheme"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:autoRemoveFromRecents="true" android:autoRemoveFromRecents="true"
android:exported="true"
android:label="@string/action_add_to_workspace" > android:label="@string/action_add_to_workspace" >
<intent-filter> <intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" /> <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
@ -165,6 +167,7 @@
android:name="com.android.launcher3.settings.SettingsActivity" android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text" android:label="@string/settings_button_text"
android:theme="@style/HomeSettingsTheme" android:theme="@style/HomeSettingsTheme"
android:exported="true"
android:autoRemoveFromRecents="true"> android:autoRemoveFromRecents="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" /> <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
@ -187,6 +190,7 @@
android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher" android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:launchMode="singleTop" android:launchMode="singleTop"
android:exported="true"
android:enabled="true"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -53,6 +53,7 @@
android:resizeableActivity="true" android:resizeableActivity="true"
android:resumeWhilePausing="true" android:resumeWhilePausing="true"
android:taskAffinity="" android:taskAffinity=""
android:exported="true"
android:enabled="true"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -164,6 +164,12 @@ public class DebugTestInformationHandler extends TestInformationHandler {
} }
case TestProtocol.REQUEST_GET_TEST_EVENTS: { case TestProtocol.REQUEST_GET_TEST_EVENTS: {
if (sEvents == null) {
// sEvents can be null if Launcher died and restarted after
// REQUEST_START_EVENT_LOGGING.
return response;
}
synchronized (sEvents) { synchronized (sEvents) {
response.putStringArrayList( response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents)); TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));

View File

@ -131,6 +131,7 @@ message Application {
// Legacy shortcuts and shortcuts handled by ShortcutManager // Legacy shortcuts and shortcuts handled by ShortcutManager
message Shortcut { message Shortcut {
optional string shortcut_name = 1; optional string shortcut_name = 1;
optional string shortcut_id = 2;
} }
// AppWidgets handled by AppWidgetManager // AppWidgets handled by AppWidgetManager

View File

@ -53,6 +53,7 @@
android:resizeableActivity="true" android:resizeableActivity="true"
android:resumeWhilePausing="true" android:resumeWhilePausing="true"
android:taskAffinity="" android:taskAffinity=""
android:exported="true"
android:enabled="true"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -17,97 +17,91 @@
** limitations under the License. ** limitations under the License.
*/ */
--> -->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
<permission <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3">
<permission
android:name="${packageName}.permission.HOTSEAT_EDU" android:name="${packageName}.permission.HOTSEAT_EDU"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem" /> android:protectionLevel="signatureOrSystem" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" /> <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" /> <uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
<application <application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:backupAgent="com.android.launcher3.LauncherBackupAgent" android:fullBackupOnly="true"
android:fullBackupOnly="true" android:fullBackupContent="@xml/backupscheme"
android:fullBackupContent="@xml/backupscheme" android:hardwareAccelerated="true"
android:hardwareAccelerated="true" android:icon="@drawable/ic_launcher_home"
android:icon="@drawable/ic_launcher_home" android:label="@string/derived_app_name"
android:label="@string/derived_app_name" android:theme="@style/AppTheme"
android:theme="@style/AppTheme" android:largeHeap="@bool/config_largeHeap"
android:largeHeap="@bool/config_largeHeap" android:restoreAnyVersion="true"
android:restoreAnyVersion="true" android:supportsRtl="true">
android:supportsRtl="true" >
<service <service android:name="com.android.quickstep.TouchInteractionService"
android:name="com.android.quickstep.TouchInteractionService" android:permission="android.permission.STATUS_BAR_SERVICE"
android:permission="android.permission.STATUS_BAR_SERVICE" android:directBootAware="true"
android:directBootAware="true" > android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.QUICKSTEP_SERVICE" /> <action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
</intent-filter> </intent-filter>
</service> </service>
<activity android:name="com.android.quickstep.RecentsActivity" <activity android:name="com.android.quickstep.RecentsActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:clearTaskOnLaunch="true" android:clearTaskOnLaunch="true"
android:stateNotNeeded="true" android:stateNotNeeded="true"
android:theme="@style/LauncherTheme" android:theme="@style/LauncherTheme"
android:screenOrientation="unspecified" android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true" android:resizeableActivity="true"
android:resumeWhilePausing="true" android:resumeWhilePausing="true"
android:taskAffinity="" /> android:taskAffinity=""/>
<!-- Content provider to settings search. The autority should be same as the packageName --> <!-- Content provider to settings search. The autority should be same as the packageName -->
<provider <provider android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
android:name="com.android.quickstep.LauncherSearchIndexablesProvider" android:authorities="${packageName}"
android:authorities="${packageName}" android:grantUriPermissions="true"
android:grantUriPermissions="true" android:multiprocess="true"
android:multiprocess="true" android:permission="android.permission.READ_SEARCH_INDEXABLES"
android:permission="android.permission.READ_SEARCH_INDEXABLES" android:exported="true">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /> <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER"/>
</intent-filter> </intent-filter>
</provider> </provider>
<!-- FileProvider used for sharing images. --> <!-- FileProvider used for sharing images. -->
<provider <provider android:name="androidx.core.content.FileProvider"
android:name="androidx.core.content.FileProvider" android:authorities="${packageName}.overview.fileprovider"
android:authorities="${packageName}.overview.fileprovider" android:exported="false"
android:exported="false" android:grantUriPermissions="true">
android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
<meta-data android:resource="@xml/overview_file_provider_paths"/>
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/overview_file_provider_paths" />
</provider> </provider>
<service <service android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService" tools:node="remove"/>
tools:node="remove" />
<activity <activity android:name="com.android.launcher3.proxy.ProxyActivityStarter"
android:name="com.android.launcher3.proxy.ProxyActivityStarter" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" android:launchMode="singleTask"
android:launchMode="singleTask" android:clearTaskOnLaunch="true"
android:clearTaskOnLaunch="true" android:exported="false"/>
android:exported="false" />
<activity <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:name="com.android.quickstep.interaction.GestureSandboxActivity" android:autoRemoveFromRecents="true"
android:autoRemoveFromRecents="true" android:excludeFromRecents="true"
android:excludeFromRecents="true" android:screenOrientation="portrait"
android:screenOrientation="portrait"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX" /> <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
@ -116,7 +110,8 @@
android:noHistory="true" android:noHistory="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:clearTaskOnLaunch="true" android:clearTaskOnLaunch="true"
android:permission="${packageName}.permission.HOTSEAT_EDU"> android:permission="${packageName}.permission.HOTSEAT_EDU"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/> <action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />

View File

@ -16,6 +16,12 @@
package com.android.launcher3.appprediction; package com.android.launcher3.appprediction;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.TargetApi; import android.annotation.TargetApi;
@ -31,29 +37,38 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FolderContainer;
import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
import com.android.launcher3.logging.StatsLogManager.EventEnum;
import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.pm.UserCache;
import com.android.systemui.plugins.AppLaunchEventsPlugin; import com.android.quickstep.logging.StatsLogCompatManager;
import com.android.systemui.plugins.PluginListener; import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
import java.util.ArrayList; import java.util.Locale;
import java.util.List; import java.util.function.Predicate;
/** /**
* Subclass of app tracker which publishes the data to the prediction engine and gets back results. * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
*/ */
@TargetApi(Build.VERSION_CODES.Q) @TargetApi(Build.VERSION_CODES.Q)
public class PredictionAppTracker extends AppLaunchTracker public class PredictionAppTracker extends AppLaunchTracker implements StatsLogConsumer {
implements PluginListener<AppLaunchEventsPlugin> {
private static final String TAG = "PredictionAppTracker"; private static final String TAG = "PredictionAppTracker";
private static final boolean DBG = false; private static final boolean DBG = false;
@ -65,11 +80,9 @@ public class PredictionAppTracker extends AppLaunchTracker
protected final Context mContext; protected final Context mContext;
private final Handler mMessageHandler; private final Handler mMessageHandler;
private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList;
// Accessed only on worker thread // Accessed only on worker thread
private AppPredictor mHomeAppPredictor; private AppPredictor mHomeAppPredictor;
private AppPredictor mRecentsOverviewPredictor;
public PredictionAppTracker(Context context) { public PredictionAppTracker(Context context) {
mContext = context; mContext = context;
@ -77,10 +90,6 @@ public class PredictionAppTracker extends AppLaunchTracker
InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged); InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
mMessageHandler.sendEmptyMessage(MSG_INIT); mMessageHandler.sendEmptyMessage(MSG_INIT);
mAppLaunchEventsPluginsList = new ArrayList<>();
PluginManagerWrapper.INSTANCE.get(context)
.addPluginListener(this, AppLaunchEventsPlugin.class, true);
} }
@UiThread @UiThread
@ -97,10 +106,7 @@ public class PredictionAppTracker extends AppLaunchTracker
mHomeAppPredictor.destroy(); mHomeAppPredictor.destroy();
mHomeAppPredictor = null; mHomeAppPredictor = null;
} }
if (mRecentsOverviewPredictor != null) { StatsLogCompatManager.LOGS_CONSUMER.remove(this);
mRecentsOverviewPredictor.destroy();
mRecentsOverviewPredictor = null;
}
} }
@WorkerThread @WorkerThread
@ -142,7 +148,7 @@ public class PredictionAppTracker extends AppLaunchTracker
// Initialize the clients // Initialize the clients
int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns; int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
mHomeAppPredictor = createPredictor(Client.HOME, count); mHomeAppPredictor = createPredictor(Client.HOME, count);
mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count); StatsLogCompatManager.LOGS_CONSUMER.add(this);
return true; return true;
} }
case MSG_DESTROY: { case MSG_DESTROY: {
@ -157,12 +163,7 @@ public class PredictionAppTracker extends AppLaunchTracker
} }
case MSG_PREDICT: { case MSG_PREDICT: {
if (mHomeAppPredictor != null) { if (mHomeAppPredictor != null) {
String client = (String) msg.obj; mHomeAppPredictor.requestPredictionUpdate();
if (Client.HOME.id.equals(client)) {
mHomeAppPredictor.requestPredictionUpdate();
} else {
mRecentsOverviewPredictor.requestPredictionUpdate();
}
} }
return true; return true;
} }
@ -179,98 +180,142 @@ public class PredictionAppTracker extends AppLaunchTracker
if (DBG) { if (DBG) {
Log.d(TAG, String.format("Sent immediate message to update %s", client)); Log.d(TAG, String.format("Sent immediate message to update %s", client));
} }
// Relay onReturnedToHome to every plugin.
mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
} }
@Override @AnyThread
@UiThread private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) {
public void onStartShortcut(String packageName, String shortcutId, UserHandle user, AppTarget target = toAppTarget(atomInfo);
String container) { if (target != null) {
// TODO: Use the full shortcut info AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
AppTarget target = new AppTarget.Builder( .setLaunchLocation(getContainer(atomInfo))
new AppTargetId("shortcut:" + shortcutId), packageName, user)
.setClassName(shortcutId)
.build();
sendLaunch(target, container);
// Relay onStartShortcut info to every connected plugin.
mAppLaunchEventsPluginsList
.forEach(plugin -> plugin.onStartShortcut(
packageName,
shortcutId,
user,
container != null ? container : CONTAINER_DEFAULT)
);
}
@Override
@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(); .build();
sendLaunch(target, container); Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
// Relay onStartApp to every connected plugin.
mAppLaunchEventsPluginsList
.forEach(plugin -> plugin.onStartApp(
cn,
user,
container != null ? container : CONTAINER_DEFAULT)
);
} }
} }
@Override @Override
@UiThread public void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
public void onDismissApp(ComponentName cn, UserHandle user, String container) { if (event == LAUNCHER_APP_LAUNCH_TAP
if (cn == null) return; || event == LAUNCHER_TASK_LAUNCH_SWIPE_DOWN
AppTarget target = new AppTarget.Builder( || event == LAUNCHER_TASK_LAUNCH_TAP
new AppTargetId("app: " + cn), cn.getPackageName(), user) || event == LAUNCHER_QUICKSWITCH_RIGHT
.setClassName(cn.getClassName()) || event == LAUNCHER_QUICKSWITCH_LEFT) {
.build(); sendEvent(atomInfo, AppTargetEvent.ACTION_LAUNCH);
sendDismiss(target, container); } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
sendEvent(atomInfo, AppTargetEvent.ACTION_DISMISS);
// Relay onDismissApp to every connected plugin. }
mAppLaunchEventsPluginsList
.forEach(plugin -> plugin.onDismissApp(
cn,
user,
container != null ? container : CONTAINER_DEFAULT)
);
} }
@UiThread @Nullable
private void sendEvent(AppTarget target, String container, int eventId) { private AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
AppTargetEvent event = new AppTargetEvent.Builder(target, eventId) UserHandle userHandle = Process.myUserHandle();
.setLaunchLocation(container == null ? CONTAINER_DEFAULT : container) if (info.getIsWork()) {
.build(); userHandle = UserCache.INSTANCE.get(mContext).getUserProfiles().stream()
Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget(); .filter(((Predicate<UserHandle>) userHandle::equals).negate())
.findAny()
.orElse(null);
}
if (userHandle == null) {
return null;
}
ComponentName cn = null;
String id = null;
switch (info.getItemCase()) {
case APPLICATION: {
LauncherAtom.Application app = info.getApplication();
if ((cn = parseNullable(app.getComponentName())) != null) {
id = "app:" + cn.getPackageName();
}
break;
}
case SHORTCUT: {
LauncherAtom.Shortcut si = info.getShortcut();
if (!TextUtils.isEmpty(si.getShortcutId())
&& (cn = parseNullable(si.getShortcutName())) != null) {
id = "shortcut:" + si.getShortcutId();
}
break;
}
case WIDGET: {
LauncherAtom.Widget widget = info.getWidget();
if ((cn = parseNullable(widget.getComponentName())) != null) {
id = "widget:" + cn.getPackageName();
}
break;
}
case TASK: {
LauncherAtom.Task task = info.getTask();
if ((cn = parseNullable(task.getComponentName())) != null) {
id = "app:" + cn.getPackageName();
}
break;
}
case FOLDER_ICON: {
id = "folder:" + SystemClock.uptimeMillis();
cn = new ComponentName(mContext.getPackageName(), "#folder");
}
}
if (id != null && cn != null) {
return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
.setClassName(cn.getClassName())
.build();
}
return null;
} }
@UiThread private String getContainer(LauncherAtom.ItemInfo info) {
private void sendLaunch(AppTarget target, String container) { ContainerInfo ci = info.getContainerInfo();
sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH); switch (ci.getContainerCase()) {
case WORKSPACE: {
// In case the item type is not widgets, the spaceX and spanY default to 1.
int spanX = info.getWidget().getSpanX();
int spanY = info.getWidget().getSpanY();
return getWorkspaceContainerString(ci.getWorkspace(), spanX, spanY);
}
case HOTSEAT: {
return getHotseatContainerString(ci.getHotseat());
}
case TASK_SWITCHER_CONTAINER: {
return "task-switcher";
}
case ALL_APPS_CONTAINER: {
return "all-apps";
}
case SEARCH_RESULT_CONTAINER: {
return "search-results";
}
case PREDICTED_HOTSEAT_CONTAINER: {
return "predictions/hotseat";
}
case PREDICTION_CONTAINER: {
return "predictions";
}
case FOLDER: {
FolderContainer fc = ci.getFolder();
switch (fc.getParentContainerCase()) {
case WORKSPACE:
return "folder/" + getWorkspaceContainerString(fc.getWorkspace(), 1, 1);
case HOTSEAT:
return "folder/" + getHotseatContainerString(fc.getHotseat());
}
return "folder";
}
}
return "";
} }
@UiThread private static String getWorkspaceContainerString(WorkspaceContainer wc, int spanX, int spanY) {
private void sendDismiss(AppTarget target, String container) { return String.format(Locale.ENGLISH, "workspace/%d/[%d,%d]/[%d,%d]",
sendEvent(target, container, AppTargetEvent.ACTION_DISMISS); wc.getPageIndex(), wc.getGridX(), wc.getGridY(), spanX, spanY);
} }
@Override private static String getHotseatContainerString(HotseatContainer hc) {
public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) { return String.format(Locale.ENGLISH, "hotseat/%d", hc.getIndex());
mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
} }
@Override private static ComponentName parseNullable(String componentNameString) {
public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) { return TextUtils.isEmpty(componentNameString)
mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin); ? null : ComponentName.unflattenFromString(componentNameString);
} }
} }

View File

@ -56,7 +56,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper; import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper; import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.ItemInfoWithIcon;
@ -93,9 +92,6 @@ public class PredictionRowView extends LinearLayout implements
private static final Interpolator ALPHA_FACTOR_INTERPOLATOR = private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
(t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f; (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
private static final OnClickListener PREDICTION_CLICK_LISTENER =
ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
private final Launcher mLauncher; private final Launcher mLauncher;
private final PredictionUiStateManager mPredictionUiStateManager; private final PredictionUiStateManager mPredictionUiStateManager;
private int mNumPredictedAppsPerRow; private int mNumPredictedAppsPerRow;
@ -246,7 +242,7 @@ public class PredictionRowView extends LinearLayout implements
while (getChildCount() < mNumPredictedAppsPerRow) { while (getChildCount() < mNumPredictedAppsPerRow) {
BubbleTextView icon = (BubbleTextView) inflater.inflate( BubbleTextView icon = (BubbleTextView) inflater.inflate(
R.layout.all_apps_icon, this, false); R.layout.all_apps_icon, this, false);
icon.setOnClickListener(PREDICTION_CLICK_LISTENER); icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS); icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeoutFactor(1f); icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mFocusHelper); icon.setOnFocusChangeListener(mFocusHelper);

View File

@ -75,8 +75,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
// TODO (b/129421797): Update the client constants // TODO (b/129421797): Update the client constants
public enum Client { public enum Client {
HOME("home"), HOME("home");
OVERVIEW("overview");
public final String id; public final String id;
@ -91,10 +90,9 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
private final Context mContext; private final Context mContext;
private final DynamicItemCache mDynamicItemCache; private final DynamicItemCache mDynamicItemCache;
private final List[] mPredictionServicePredictions; private List mPredictionServicePredictions = Collections.emptyList();
private int mMaxIconsPerRow; private int mMaxIconsPerRow;
private Client mActiveClient;
private AllAppsContainerView mAppsView; private AllAppsContainerView mAppsView;
@ -108,16 +106,10 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated); mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
mActiveClient = Client.HOME;
InvariantDeviceProfile idp = LauncherAppState.getIDP(context); InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
mMaxIconsPerRow = idp.numColumns; mMaxIconsPerRow = idp.numColumns;
idp.addOnChangeListener(this); idp.addOnChangeListener(this);
mPredictionServicePredictions = new List[Client.values().length];
for (int i = 0; i < mPredictionServicePredictions.length; i++) {
mPredictionServicePredictions[i] = Collections.emptyList();
}
mGettingValidPredictionResults = Utilities.getDevicePrefs(context) mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
.getBoolean(LAST_PREDICTION_ENABLED_STATE, true); .getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
@ -130,18 +122,6 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
mMaxIconsPerRow = profile.numColumns; mMaxIconsPerRow = profile.numColumns;
} }
public Client getClient() {
return mActiveClient;
}
public void switchClient(Client client) {
if (client == mActiveClient) {
return;
}
mActiveClient = client;
dispatchOnChange(true);
}
public void setTargetAppsView(AllAppsContainerView appsView) { public void setTargetAppsView(AllAppsContainerView appsView) {
if (mAppsView != null) { if (mAppsView != null) {
mAppsView.getAppsStore().removeUpdateListener(this); mAppsView.getAppsStore().removeUpdateListener(this);
@ -195,10 +175,8 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
} }
private void updatePredictionStateAfterCallback() { private void updatePredictionStateAfterCallback() {
boolean validResults = false; boolean validResults = mPredictionServicePredictions != null
for (List l : mPredictionServicePredictions) { && !mPredictionServicePredictions.isEmpty();
validResults |= l != null && !l.isEmpty();
}
if (validResults != mGettingValidPredictionResults) { if (validResults != mGettingValidPredictionResults) {
mGettingValidPredictionResults = validResults; mGettingValidPredictionResults = validResults;
Utilities.getDevicePrefs(mContext).edit() Utilities.getDevicePrefs(mContext).edit()
@ -210,7 +188,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
public AppPredictor.Callback appPredictorCallback(Client client) { public AppPredictor.Callback appPredictorCallback(Client client) {
return targets -> { return targets -> {
mPredictionServicePredictions[client.ordinal()] = targets; mPredictionServicePredictions = targets;
updatePredictionStateAfterCallback(); updatePredictionStateAfterCallback();
}; };
} }
@ -238,7 +216,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
state.apps = new ArrayList<>(); state.apps = new ArrayList<>();
List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()]; List<AppTarget> appTargets = mPredictionServicePredictions;
if (!appTargets.isEmpty()) { if (!appTargets.isEmpty()) {
for (AppTarget appTarget : appTargets) { for (AppTarget appTarget : appTargets) {
ComponentKey key; ComponentKey key;

View File

@ -34,8 +34,6 @@ import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
@ -129,12 +127,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
} }
@Override @Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item, public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
@Nullable String sourceContainer) {
if (mHotseatPredictionController != null) { if (mHotseatPredictionController != null) {
mHotseatPredictionController.setPauseUIUpdate(true); mHotseatPredictionController.setPauseUIUpdate(true);
} }
return super.startActivitySafely(v, intent, item, sourceContainer); return super.startActivitySafely(v, intent, item);
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log; import android.util.Log;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
@ -52,17 +53,17 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
private final BaseActivityInterface<?, T> mActivityInterface; private final BaseActivityInterface<?, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview. // The id of the currently running task that is transitioning to overview.
private final int mTargetTaskId; private final RunningTaskInfo mTargetTask;
private final RecentsAnimationDeviceState mDeviceState; private final RecentsAnimationDeviceState mDeviceState;
private T mActivity; private T mActivity;
private RecentsView mRecentsView; private RecentsView mRecentsView;
AppToOverviewAnimationProvider( AppToOverviewAnimationProvider(
BaseActivityInterface<?, T> activityInterface, int targetTaskId, BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
RecentsAnimationDeviceState deviceState) { RecentsAnimationDeviceState deviceState) {
mActivityInterface = activityInterface; mActivityInterface = activityInterface;
mTargetTaskId = targetTaskId; mTargetTask = targetTask;
mDeviceState = deviceState; mDeviceState = deviceState;
} }
@ -73,7 +74,7 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
* @param wasVisible true if it was visible before * @param wasVisible true if it was visible before
*/ */
boolean onActivityReady(T activity, Boolean wasVisible) { boolean onActivityReady(T activity, Boolean wasVisible) {
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId); activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible); AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI( BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState, mDeviceState,
@ -122,7 +123,8 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
wallpaperTargets, MODE_CLOSING); wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation // Use the top closing app to determine the insets for the animation
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId); RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
: targets.findTask(mTargetTask.taskId);
if (runningTaskTarget == null) { if (runningTaskTarget == null) {
Log.e(TAG, "No closing app"); Log.e(TAG, "No closing app");
return pa.buildAnim(); return pa.buildAnim();

View File

@ -15,6 +15,8 @@
*/ */
package com.android.quickstep; package com.android.quickstep;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
@ -27,11 +29,13 @@ import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.Toast;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.VibratorWrapper;
@ -138,10 +142,10 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
mRecentsView.getNextPageTaskView().launchTask(false /* animate */, mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */); true /* freezeTaskList */);
} else { } else {
int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
if (!mCanceled) { if (!mCanceled) {
TaskView nextTask = mRecentsView.getTaskView(taskId); TaskView nextTask = mRecentsView.getNextPageTaskView();
if (nextTask != null) { if (nextTask != null) {
int taskId = nextTask.getTask().key.id;
mGestureState.updateLastStartedTaskId(taskId); mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId); .contains(taskId);
@ -158,6 +162,10 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
mRecentsAnimationController.finish(true /* toRecents */, null); mRecentsAnimationController.finish(true /* toRecents */, null);
} }
}, MAIN_EXECUTOR.getHandler()); }, MAIN_EXECUTOR.getHandler());
} else {
mActivityInterface.onLaunchTaskFailed();
Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
mRecentsAnimationController.finish(true /* toRecents */, null);
} }
} }
mCanceled = false; mCanceled = false;

View File

@ -38,7 +38,6 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@ -60,6 +59,7 @@ import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingView;
@ -70,6 +70,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@ -266,10 +267,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK, mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled); this::notifyTransitionCancelled);
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
() -> mDeviceState.onEndTargetCalculated(mGestureState.getEndTarget(),
mActivityInterface));
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
@ -313,12 +310,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
} }
setupRecentsViewUi(); setupRecentsViewUi();
if (mDeviceState.getNavMode() == TWO_BUTTONS) {
// If the device is in two button mode, swiping up will show overview with predictions
// so we need to kick off switching to the overview predictions as soon as possible
mActivityInterface.updateOverviewPredictionState();
}
linkRecentsViewScroll(); linkRecentsViewScroll();
return true; return true;
@ -400,6 +391,11 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback( mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch); mOnDeferredActivityLaunch);
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
() -> mDeviceState.getRotationTouchHelper().
onEndTargetCalculated(mGestureState.getEndTarget(),
mActivityInterface));
notifyGestureStartedAsync(); notifyGestureStartedAsync();
} }
@ -423,7 +419,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
} }
protected void notifyGestureAnimationStartToRecents() { protected void notifyGestureAnimationStartToRecents() {
mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId()); mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
} }
private void launcherFrameDrawn() { private void launcherFrameDrawn() {
@ -451,12 +447,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
@Override @Override
public void onMotionPauseChanged(boolean isPaused) { public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION); setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
// In fully gestural nav mode, switch to overview predictions once the user has paused
// (this is a no-op if the predictions are already in that state)
mActivityInterface.updateOverviewPredictionState();
}
} }
public void maybeUpdateRecentsAttachedState() { public void maybeUpdateRecentsAttachedState() {
@ -555,14 +545,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
@Override @Override
public void updateFinalShift() { public void updateFinalShift() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentCropRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
}
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) { if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed; mPassedOverviewThreshold = passed;
@ -573,6 +555,14 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
updateSysUiFlags(mCurrentShift.value); updateSysUiFlags(mCurrentShift.value);
applyWindowTransform(); applyWindowTransform();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
}
updateLauncherTransitionProgress(); updateLauncherTransitionProgress();
} }
@ -889,22 +879,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs); animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
} }
private void doLogGesture(GestureEndTarget endTarget) { private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
DeviceProfile dp = mDp;
if (dp == null || mDownPos == null) {
// We probably never received an animation controller, skip logging.
return;
}
int pageIndex = endTarget == LAST_TASK
? LOG_NO_OP_PAGE_INDEX
: mRecentsView.getNextPage();
UserEventDispatcher.newInstance(mContext).logStateChangeAction(
mLogAction, mLogDirection,
(int) mDownPos.x, (int) mDownPos.y,
ContainerType.NAVBAR, ContainerType.APP,
endTarget.containerType,
pageIndex);
StatsLogManager.EventEnum event; StatsLogManager.EventEnum event;
switch (endTarget) { switch (endTarget) {
case HOME: case HOME:
@ -922,10 +897,29 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
default: default:
event = IGNORE; event = IGNORE;
} }
StatsLogManager.newInstance(mContext).logger() StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
.withSrcState(LAUNCHER_STATE_BACKGROUND) .withSrcState(LAUNCHER_STATE_BACKGROUND)
.withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType)) .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType));
.log(event); if (targetTask != null) {
logger.withItemInfo(targetTask.getItemInfo());
}
logger.log(event);
DeviceProfile dp = mDp;
if (dp == null || mDownPos == null) {
// We probably never received an animation controller, skip logging.
return;
}
int pageIndex = endTarget == LAST_TASK
? LOG_NO_OP_PAGE_INDEX
: mRecentsView.getNextPage();
UserEventDispatcher.newInstance(mContext).logStateChangeAction(
mLogAction, mLogDirection,
(int) mDownPos.x, (int) mDownPos.y,
ContainerType.NAVBAR, ContainerType.APP,
endTarget.containerType,
pageIndex);
} }
/** Animates to the given progress, where 0 is the current app and 1 is overview. */ /** Animates to the given progress, where 0 is the current app and 1 is overview. */
@ -1130,7 +1124,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
private void resumeLastTask() { private void resumeLastTask() {
mRecentsAnimationController.finish(false /* toRecents */, null); mRecentsAnimationController.finish(false /* toRecents */, null);
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
doLogGesture(LAST_TASK); doLogGesture(LAST_TASK, null);
reset(); reset();
} }
@ -1145,6 +1139,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
@UiThread @UiThread
private void startNewTaskInternal() { private void startNewTaskInternal() {
TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
startNewTask(success -> { startNewTask(success -> {
if (!success) { if (!success) {
reset(); reset();
@ -1153,7 +1148,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
endLauncherTransitionController(); endLauncherTransitionController();
updateSysUiFlags(1 /* windowProgress == overview */); updateSysUiFlags(1 /* windowProgress == overview */);
} }
doLogGesture(NEW_TASK); doLogGesture(NEW_TASK, taskToLaunch);
}); });
} }
@ -1298,7 +1293,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
} }
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
doLogGesture(HOME); doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
} }
protected abstract void finishRecentsControllerToHome(Runnable callback); protected abstract void finishRecentsControllerToHome(Runnable callback);
@ -1313,7 +1308,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
mRecentsView.onSwipeUpAnimationSuccess(); mRecentsView.onSwipeUpAnimationSuccess();
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS); doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset(); reset();
} }

View File

@ -140,7 +140,7 @@ public final class FallbackActivityInterface extends
} }
@Override @Override
public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) { public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
// no-op, fake landscape not supported for 3P // no-op, fake landscape not supported for 3P
} }

View File

@ -140,7 +140,6 @@ public class FallbackSwipeHandler extends
private final long mDuration; private final long mDuration;
FallbackHomeAnimationFactory(long duration) { FallbackHomeAnimationFactory(long duration) {
super(null);
mDuration = duration; mDuration = duration;
if (mRunningOverHome) { if (mRunningOverHome) {

View File

@ -105,7 +105,7 @@ public final class LauncherActivityInterface extends
// recents, we assume the first task is invisible, making translation off by one task. // recents, we assume the first task is invisible, making translation off by one task.
launcher.getStateManager().reapplyState(); launcher.getStateManager().reapplyState();
launcher.getRootView().setForceHideBackArrow(false); launcher.getRootView().setForceHideBackArrow(false);
notifyRecentsOfOrientation(deviceState); notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
} }
@Override @Override
@ -120,7 +120,7 @@ public final class LauncherActivityInterface extends
@Override @Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) { boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
notifyRecentsOfOrientation(deviceState); notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) { DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override @Override
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
@ -228,7 +228,7 @@ public final class LauncherActivityInterface extends
@Override @Override
public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) { public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
final StateManager<LauncherState> stateManager = getCreatedActivity().getStateManager(); final StateManager<LauncherState> stateManager = getCreatedActivity().getStateManager();
stateManager.addStateListener( stateManager.addStateListener(
new StateManager.StateListener<LauncherState>() { new StateManager.StateListener<LauncherState>() {
@ -244,11 +244,11 @@ public final class LauncherActivityInterface extends
}); });
} }
private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) { private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
// reset layout on swipe to home // reset layout on swipe to home
RecentsView recentsView = getCreatedActivity().getOverviewPanel(); RecentsView recentsView = getCreatedActivity().getOverviewPanel();
recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(), recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
deviceState.getDisplayRotation()); rotationTouchHelper.getDisplayRotation());
} }
@Override @Override
@ -261,16 +261,6 @@ public final class LauncherActivityInterface extends
return true; return true;
} }
@Override
public void updateOverviewPredictionState() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
PredictionUiStateManager.Client.OVERVIEW);
}
@Override @Override
public int getContainerType() { public int getContainerType() {
final Launcher launcher = getVisibleLauncher(); final Launcher launcher = getVisibleLauncher();

View File

@ -16,6 +16,7 @@
package com.android.quickstep; package com.android.quickstep;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.content.Context; import android.content.Context;
@ -28,6 +29,7 @@ import androidx.annotation.NonNull;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView; import com.android.quickstep.views.TaskView;
@ -72,36 +74,39 @@ public class LauncherSwipeHandlerV2 extends
mActivity.getRootView().setForceHideBackArrow(true); mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive(); mActivity.setHintUserWillBeActive();
homeAnimFactory = new HomeAnimationFactory(floatingIconView) { if (canUseWorkspaceView) {
// We want the window alpha to be 0 once this threshold is met, so that the
@Override // FolderIconView can be seen morphing into the icon shape.
public RectF getWindowTargetRect() { float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
if (canUseWorkspaceView) { homeAnimFactory = new LauncherHomeAnimationFactory() {
@Override
public RectF getWindowTargetRect() {
return iconLocation; return iconLocation;
} else {
return super.getWindowTargetRect();
} }
}
@NonNull @Override
@Override public void setAnimation(RectFSpringAnim anim) {
public AnimatorPlaybackController createActivityAnimationToHome() { anim.addAnimatorListener(floatingIconView);
// Return an empty APC here since we have an non-user controlled animation floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
// to home. floatingIconView.setFastFinishRunnable(anim::end);
long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx); }
return mActivity.getStateManager().createAnimationToNewWorkspace(
NORMAL, accuracy, 0 /* animComponents */);
}
@Override @Override
public void playAtomicAnimation(float velocity) { public void update(RectF currentRect, float progress, float radius) {
new StaggeredWorkspaceAnim(mActivity, velocity, floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
true /* animateOverviewScrim */).start(); radius, false);
} }
};
@Override
public void onCancel() {
floatingIconView.fastFinish();
}
};
} else {
homeAnimFactory = new LauncherHomeAnimationFactory();
}
} else { } else {
homeAnimFactory = new HomeAnimationFactory(null) { homeAnimFactory = new HomeAnimationFactory() {
@Override @Override
public AnimatorPlaybackController createActivityAnimationToHome() { public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@ -118,4 +123,22 @@ public class LauncherSwipeHandlerV2 extends
mRecentsAnimationController.finish( mRecentsAnimationController.finish(
true /* toRecents */, callback, true /* sendUserLeaveHint */); true /* toRecents */, callback, true /* sendUserLeaveHint */);
} }
private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
@NonNull
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
// Return an empty APC here since we have an non-user controlled animation
// to home.
long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
return mActivity.getStateManager().createAnimationToNewWorkspace(
NORMAL, accuracy, 0 /* animComponents */);
}
@Override
public void playAtomicAnimation(float velocity) {
new StaggeredWorkspaceAnim(mActivity, velocity,
true /* animateOverviewScrim */).start();
}
}
} }

View File

@ -29,7 +29,6 @@ import android.view.ViewConfiguration;
import androidx.annotation.BinderThread; import androidx.annotation.BinderThread;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
@ -165,7 +164,7 @@ public class OverviewCommandHelper {
mActivityInterface = mOverviewComponentObserver.getActivityInterface(); mActivityInterface = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime(); mCreateTime = SystemClock.elapsedRealtime();
mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface, mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
RecentsModel.getRunningTaskId(), mDeviceState); ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan // Preload the plan
mRecentsModel.getTasks(null); mRecentsModel.getTasks(null);
@ -227,10 +226,6 @@ public class OverviewCommandHelper {
LauncherLogProto.ContainerType.TASKSWITCHER); LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true; mUserEventLogged = true;
} }
// Switch prediction client to overview
PredictionUiStateManager.INSTANCE.get(activity).switchClient(
PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible); return mAnimationProvider.onActivityReady(activity, wasVisible);
} }

View File

@ -17,7 +17,6 @@ package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator; import android.animation.Animator;
import android.content.Context; import android.content.Context;
@ -28,7 +27,6 @@ import android.graphics.RectF;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
@ -37,7 +35,6 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams; import com.android.quickstep.util.TransformParams;
@ -85,7 +82,8 @@ public abstract class SwipeUpAnimationLogic {
mTransformParams = transformParams; mTransformParams = transformParams;
mTaskViewSimulator.setLayoutRotation( mTaskViewSimulator.setLayoutRotation(
mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation()); mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation());
} }
protected void initTransitionEndpoints(DeviceProfile dp) { protected void initTransitionEndpoints(DeviceProfile dp) {
@ -148,12 +146,6 @@ public abstract class SwipeUpAnimationLogic {
protected abstract class HomeAnimationFactory { protected abstract class HomeAnimationFactory {
public FloatingIconView mIconView;
public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
mIconView = iconView;
}
public @NonNull RectF getWindowTargetRect() { public @NonNull RectF getWindowTargetRect() {
PagedOrientationHandler orientationHandler = getOrientationHandler(); PagedOrientationHandler orientationHandler = getOrientationHandler();
DeviceProfile dp = mDp; DeviceProfile dp = mDp;
@ -174,6 +166,12 @@ public abstract class SwipeUpAnimationLogic {
public void playAtomicAnimation(float velocity) { public void playAtomicAnimation(float velocity) {
// No-op // No-op
} }
public void setAnimation(RectFSpringAnim anim) { }
public void update(RectF currentRect, float progress, float radius) { }
public void onCancel() { }
} }
/** /**
@ -184,8 +182,6 @@ public abstract class SwipeUpAnimationLogic {
protected RectFSpringAnim createWindowAnimationToHome(float startProgress, protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) { HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
final FloatingIconView fiv = homeAnimationFactory.mIconView;
final boolean isFloatingIconView = fiv != null;
mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress)); mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
@ -203,11 +199,7 @@ public abstract class SwipeUpAnimationLogic {
windowToHomePositionMap.mapRect(startRect); windowToHomePositionMap.mapRect(startRect);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
if (isFloatingIconView) { homeAnimationFactory.setAnimation(anim);
anim.addAnimatorListener(fiv);
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
fiv.setFastFinishRunnable(anim::end);
}
SpringAnimationRunner runner = new SpringAnimationRunner( SpringAnimationRunner runner = new SpringAnimationRunner(
homeAnimationFactory, cropRectF, homeToWindowPositionMap); homeAnimationFactory, cropRectF, homeToWindowPositionMap);
@ -242,32 +234,27 @@ public abstract class SwipeUpAnimationLogic {
final RectF mWindowCurrentRect = new RectF(); final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap; final Matrix mHomeToWindowPositionMap;
final HomeAnimationFactory mAnimationFactory;
final FloatingIconView mFIV;
final AnimatorPlaybackController mHomeAnim; final AnimatorPlaybackController mHomeAnim;
final RectF mCropRectF; final RectF mCropRectF;
final float mStartRadius; final float mStartRadius;
final float mEndRadius; final float mEndRadius;
final float mWindowAlphaThreshold;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
Matrix homeToWindowPositionMap) { Matrix homeToWindowPositionMap) {
mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome(); mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF; mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap; mHomeToWindowPositionMap = homeToWindowPositionMap;
cropRectF.roundOut(mCropRect); cropRectF.roundOut(mCropRect);
mFIV = factory.mIconView;
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation. // rounding at the end of the animation.
mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
mEndRadius = cropRectF.width() / 2f; mEndRadius = cropRectF.width() / 2f;
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
} }
@Override @Override
@ -282,10 +269,7 @@ public abstract class SwipeUpAnimationLogic {
.setCornerRadius(cornerRadius); .setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
if (mFIV != null) { mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
mFIV.update(currentRect, 1f, progress,
mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
}
} }
@Override @Override
@ -298,9 +282,7 @@ public abstract class SwipeUpAnimationLogic {
@Override @Override
public void onCancel() { public void onCancel() {
if (mFIV != null) { mAnimationFactory.onCancel();
mFIV.fastFinish();
}
} }
@Override @Override

View File

@ -19,6 +19,7 @@ package com.android.quickstep;
import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED; import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@ -146,26 +147,29 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
*/ */
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix, public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
boolean rotated) { boolean rotated) {
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot; getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated); if (thumbnail != null) {
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
getActionsView().setCallbacks(new OverlayUICallbacks() { getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override @Override
public void onShare() { public void onShare() {
if (isAllowedByPolicy) { if (isAllowedByPolicy) {
mImageApi.startShareActivity(); mImageApi.startShareActivity();
} else { } else {
showBlockedByPolicyMessage(); showBlockedByPolicyMessage();
}
} }
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Override @Override
public void onScreenshot() { public void onScreenshot() {
saveScreenshot(task); saveScreenshot(task);
} }
}); });
}
} }
/** /**

View File

@ -61,7 +61,6 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestLogging;
@ -258,6 +257,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
private static boolean sConnected = false; private static boolean sConnected = false;
private static boolean sIsInitialized = false; private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
public static boolean isConnected() { public static boolean isConnected() {
return sConnected; return sConnected;
@ -298,6 +298,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
mDeviceState = new RecentsAnimationDeviceState(this); mDeviceState = new RecentsAnimationDeviceState(this);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked); mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
ProtoTracer.INSTANCE.get(this).add(this); ProtoTracer.INSTANCE.get(this).add(this);
sConnected = true; sConnected = true;
@ -326,7 +327,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent); mMainChoreographer, this::onInputEvent);
mDeviceState.updateGestureTouchRegions(); mRotationTouchHelper.updateGestureTouchRegions();
} }
/** /**
@ -470,9 +471,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
if (TestProtocol.sDebugTracing) { if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN"); Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN");
} }
mDeviceState.setOrientationTransformIfNeeded(event); mRotationTouchHelper.setOrientationTransformIfNeeded(event);
if (mDeviceState.isInSwipeUpTouchRegion(event)) { if (mRotationTouchHelper.isInSwipeUpTouchRegion(event)) {
if (TestProtocol.sDebugTracing) { if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME, Log.d(TestProtocol.NO_SWIPE_TO_HOME,
"TouchInteractionService.onInputEvent:isInSwipeUpTouchRegion"); "TouchInteractionService.onInputEvent:isInSwipeUpTouchRegion");
@ -509,7 +510,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
// Other events // Other events
if (mUncheckedConsumer != InputConsumer.NO_OP) { if (mUncheckedConsumer != InputConsumer.NO_OP) {
// Only transform the event if we are handling it in a proper consumer // Only transform the event if we are handling it in a proper consumer
mDeviceState.setOrientationTransformIfNeeded(event); mRotationTouchHelper.setOrientationTransformIfNeeded(event);
} }
} }
@ -547,7 +548,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
gestureState.updatePreviouslyAppearedTaskIds( gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds()); previousGestureState.getPreviouslyAppearedTaskIds());
} else { } else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0", gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */))); () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
} }
return gestureState; return gestureState;
@ -660,7 +661,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) { if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
// In the case where we are in the excluded assistant state, ignore it and treat the // In the case where we are in the excluded assistant state, ignore it and treat the
// running activity as the task behind the assistant // running activity as the task behind the assistant
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant", gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.assistant",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); () -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
ComponentName runningComponent = ComponentName runningComponent =
@ -771,13 +772,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
mOverviewComponentObserver.getActivityInterface(); mOverviewComponentObserver.getActivityInterface();
final Intent overviewIntent = new Intent( final Intent overviewIntent = new Intent(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState()); mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
if (activityInterface.getCreatedActivity() == null) { if (activityInterface.getCreatedActivity() != null && fromInit) {
// Make sure that UI states will be initialized.
activityInterface.createActivityInitListener((wasVisible) -> {
AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
return false;
}).register(overviewIntent);
} else if (fromInit) {
// The activity has been created before the initialization of overview service. It is // The activity has been created before the initialization of overview service. It is
// usually happens when booting or launcher is the top activity, so we should already // usually happens when booting or launcher is the top activity, so we should already
// have the latest state. // have the latest state.

View File

@ -63,12 +63,6 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
mActivity.startHome(); mActivity.startHome();
} }
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
// Just use the activity task size for multi-window as well.
return false;
}
/** /**
* When starting gesture interaction from home, we add a temporary invisible tile corresponding * When starting gesture interaction from home, we add a temporary invisible tile corresponding
* to the home task. This allows us to handle quick-switch similarly to a quick-switching * to the home task. This allows us to handle quick-switch similarly to a quick-switching
@ -76,7 +70,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
*/ */
public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) { public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
mHomeTaskInfo = homeTaskInfo; mHomeTaskInfo = homeTaskInfo;
onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId); onGestureAnimationStart(homeTaskInfo);
} }
/** /**
@ -107,14 +101,15 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
} }
@Override @Override
protected boolean shouldAddDummyTaskView(int runningTaskId) { protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) { && getTaskViewCount() == 0) {
// Do not add a dummy task if we are running over home with empty recents, so that we // Do not add a dummy task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a dummy task and later removing it. // show the empty recents message instead of showing a dummy task and later removing it.
return false; return false;
} }
return super.shouldAddDummyTaskView(runningTaskId); return super.shouldAddDummyTaskView(runningTaskInfo);
} }
@Override @Override

View File

@ -99,7 +99,8 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer {
case ACTION_POINTER_DOWN: { case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) { if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex(); int pointerIndex = ev.getActionIndex();
if (mDeviceState.isInSwipeUpTouchRegion(ev, pointerIndex) if (mDeviceState.getRotationTouchHelper()
.isInSwipeUpTouchRegion(ev, pointerIndex)
&& mDelegate.allowInterceptByParent()) { && mDelegate.allowInterceptByParent()) {
setActive(ev); setActive(ev);

View File

@ -147,7 +147,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
if (!mThresholdCrossed) { if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction // Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex(); int ptrIdx = ev.getActionIndex();
if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) { if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction(); int action = ev.getAction();
ev.setAction(ACTION_CANCEL); ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev); finishTouchTracking(ev);

View File

@ -59,6 +59,7 @@ import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer; import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState; import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher; import com.android.quickstep.util.CachedEventDispatcher;
@ -86,6 +87,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
private final NavBarPosition mNavBarPosition; private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager; private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState; private final GestureState mGestureState;
private final RotationTouchHelper mRotationTouchHelper;
private RecentsAnimationCallbacks mActiveCallbacks; private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final InputMonitorCompat mInputMonitorCompat; private final InputMonitorCompat mInputMonitorCompat;
@ -163,6 +165,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture; mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe; mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
} }
@Override @Override
@ -230,7 +233,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (!mPassedPilferInputSlop) { if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction // Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex(); int ptrIdx = ev.getActionIndex();
if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) { if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev); forceCancelGesture(ev);
} }
} }
@ -424,7 +427,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
@Override @Override
public void notifyOrientationSetup() { public void notifyOrientationSetup() {
mDeviceState.onStartGesture(); mRotationTouchHelper.onStartGesture();
} }
@Override @Override

View File

@ -205,13 +205,15 @@ public class StaggeredWorkspaceAnim {
ResourceProvider rp = DynamicResource.provider(v.getContext()); ResourceProvider rp = DynamicResource.provider(v.getContext());
float stiffness = rp.getFloat(R.dimen.staggered_stiffness); float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
float damping = rp.getFloat(R.dimen.staggered_damping_ratio); float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
float endTransY = 0;
float springVelocity = Math.abs(mVelocity) * Math.signum(endTransY - mSpringTransY);
ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext()) ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext())
.setStiffness(stiffness) .setStiffness(stiffness)
.setDampingRatio(damping) .setDampingRatio(damping)
.setMinimumVisibleChange(1f) .setMinimumVisibleChange(1f)
.setStartValue(mSpringTransY) .setStartValue(mSpringTransY)
.setEndValue(0) .setEndValue(endTransY)
.setStartVelocity(mVelocity) .setStartVelocity(springVelocity)
.build(v, VIEW_TRANSLATE_Y); .build(v, VIEW_TRANSLATE_Y);
springTransY.setStartDelay(startDelay); springTransY.setStartDelay(startDelay);
springTransY.addListener(new AnimatorListenerAdapter() { springTransY.addListener(new AnimatorListenerAdapter() {

View File

@ -15,6 +15,7 @@
*/ */
package com.android.quickstep.util; package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.deltaRotation; import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE; import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation; import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
@ -197,6 +198,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
return mTempRectF; return mTempRectF;
} }
/**
* Returns the current task bounds in the Launcher coordinate space.
*/
public RectF getCurrentRect() {
RectF result = getCurrentCropRect();
mMatrix.mapRect(result);
return result;
}
public RecentsOrientedState getOrientationState() { public RecentsOrientedState getOrientationState() {
return mOrientationState; return mOrientationState;
} }
@ -295,6 +305,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
builder.withMatrix(mMatrix) builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect) .withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius()); .withCornerRadius(getCurrentCornerRadius());
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
builder.withRelativeLayerTo(params.getRecentsSurface(), Integer.MAX_VALUE);
}
} }
/** /**

View File

@ -16,6 +16,7 @@
package com.android.quickstep.util; package com.android.quickstep.util;
import android.util.FloatProperty; import android.util.FloatProperty;
import android.view.SurfaceControl;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
@ -58,6 +59,7 @@ public class TransformParams {
private float mCornerRadius; private float mCornerRadius;
private RemoteAnimationTargets mTargetSet; private RemoteAnimationTargets mTargetSet;
private SurfaceTransactionApplier mSyncTransactionApplier; private SurfaceTransactionApplier mSyncTransactionApplier;
private SurfaceControl mRecentsSurface;
private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@ -138,6 +140,8 @@ public class TransformParams {
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet; RemoteAnimationTargets targets = mTargetSet;
SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length]; SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
mRecentsSurface = getRecentsSurface(targets);
for (int i = 0; i < targets.unfilteredApps.length; i++) { for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i]; RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash); SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
@ -165,6 +169,20 @@ public class TransformParams {
return surfaceParams; return surfaceParams;
} }
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
if (app.mode == targets.targetMode) {
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
return app.leash.getSurfaceControl();
}
} else {
return app.leash.getSurfaceControl();
}
}
return null;
}
// Pubic getters so outside packages can read the values. // Pubic getters so outside packages can read the values.
public float getProgress() { public float getProgress() {
@ -179,6 +197,10 @@ public class TransformParams {
return mCornerRadius; return mCornerRadius;
} }
public SurfaceControl getRecentsSurface() {
return mRecentsSurface;
}
public RemoteAnimationTargets getTargetSet() { public RemoteAnimationTargets getTargetSet() {
return mTargetSet; return mTargetSet;
} }

View File

@ -16,6 +16,7 @@
package com.android.quickstep.views; package com.android.quickstep.views;
import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
@ -42,8 +43,10 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
@ -51,6 +54,7 @@ import com.android.quickstep.util.MultiValueUpdateListener;
/** /**
* View used to educate the user on how to access All Apps when in No Nav Button navigation mode. * View used to educate the user on how to access All Apps when in No Nav Button navigation mode.
* Consumes all touches until after the animation is completed and the view is removed.
*/ */
public class AllAppsEduView extends AbstractFloatingView { public class AllAppsEduView extends AbstractFloatingView {
@ -110,9 +114,19 @@ public class AllAppsEduView extends AbstractFloatingView {
return (type & TYPE_ALL_APPS_EDU) != 0; return (type & TYPE_ALL_APPS_EDU) != 0;
} }
@Override
public boolean onBackPressed() {
return true;
}
@Override
public boolean canInterceptEventsInSystemGestureRegion() {
return true;
}
@Override @Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) { public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return mAnimation != null && mAnimation.isRunning(); return true;
} }
private void playAnimation() { private void playAnimation() {
@ -139,7 +153,12 @@ public class AllAppsEduView extends AbstractFloatingView {
config.userControlled = false; config.userControlled = false;
AnimatorPlaybackController stateAnimationController = AnimatorPlaybackController stateAnimationController =
mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config); mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config);
float maxAllAppsProgress = 0.15f; float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f;
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
PendingAnimation allAppsAlpha = new PendingAnimation(config.duration);
allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha);
mAnimation.play(allAppsAlpha.buildAnim());
ValueAnimator intro = ValueAnimator.ofFloat(0, 1f); ValueAnimator intro = ValueAnimator.ofFloat(0, 1f);
intro.setInterpolator(LINEAR); intro.setInterpolator(LINEAR);
@ -191,7 +210,8 @@ public class AllAppsEduView extends AbstractFloatingView {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mAnimation = null; mAnimation = null;
stateAnimationController.dispatchOnCancel(); // Handles cancelling the animation used to hint towards All Apps.
mLauncher.getStateManager().goToState(NORMAL, false);
handleClose(false); handleClose(false);
} }
}); });

View File

@ -31,7 +31,6 @@ import android.animation.ObjectAnimator;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.UserHandle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface; import android.view.Surface;
@ -41,20 +40,15 @@ import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat; import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView; import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.TransformParams; import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard; import com.android.systemui.plugins.RecentsExtraCard;
import com.android.systemui.shared.recents.model.Task;
/** /**
* {@link RecentsView} used in Launcher activity * {@link RecentsView} used in Launcher activity
@ -180,18 +174,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
super.onTaskLaunchAnimationEnd(success); super.onTaskLaunchAnimationEnd(success);
} }
@Override
public void onTaskLaunched(Task task) {
UserHandle user = UserHandle.of(task.key.userId);
AppLaunchTracker.INSTANCE.get(getContext()).onStartApp(task.getTopComponent(), user,
AppLaunchTracker.CONTAINER_OVERVIEW);
}
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
}
@Override @Override
public void scrollTo(int x, int y) { public void scrollTo(int x, int y) {
super.scrollTo(x, y); super.scrollTo(x, y);
@ -237,9 +219,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
super.reset(); super.reset();
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0); setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
// We are moving to home or some other UI with no recents. Switch back to the home client,
// the home predictions should have been updated when the activity was resumed.
PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
} }
@Override @Override

View File

@ -70,12 +70,14 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
@IntDef(flag = true, value = { @IntDef(flag = true, value = {
DISABLED_SCROLLING, DISABLED_SCROLLING,
DISABLED_ROTATED}) DISABLED_ROTATED,
DISABLED_NO_THUMBNAIL})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface ActionsDisabledFlags { } public @interface ActionsDisabledFlags { }
public static final int DISABLED_SCROLLING = 1 << 0; public static final int DISABLED_SCROLLING = 1 << 0;
public static final int DISABLED_ROTATED = 1 << 1; public static final int DISABLED_ROTATED = 1 << 1;
public static final int DISABLED_NO_THUMBNAIL = 1 << 2;
private static final int INDEX_CONTENT_ALPHA = 0; private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_VISIBILITY_ALPHA = 1; private static final int INDEX_VISIBILITY_ALPHA = 1;

View File

@ -55,10 +55,8 @@ import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Point; import android.graphics.Point;
@ -135,6 +133,7 @@ import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LauncherEventUtil; import com.android.systemui.shared.system.LauncherEventUtil;
@ -147,7 +146,7 @@ import java.util.function.Consumer;
/** /**
* A list of recent tasks. * A list of recent tasks.
*/ */
@TargetApi(Build.VERSION_CODES.P) @TargetApi(Build.VERSION_CODES.R)
public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback, Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener, InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
@ -377,7 +376,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
mOrientationState.setMultiWindowMode(inMultiWindowMode); mOrientationState.setMultiWindowMode(inMultiWindowMode);
setLayoutRotation(mOrientationState.getTouchRotation(), setLayoutRotation(mOrientationState.getTouchRotation(),
mOrientationState.getDisplayRotation()); mOrientationState.getDisplayRotation());
rotateAllChildTasks(); updateChildTaskOrientations();
} }
if (!inMultiWindowMode && mOverviewStateEnabled) { if (!inMultiWindowMode && mOverviewStateEnabled) {
// TODO: Re-enable layout transitions for addition of the unpinned task // TODO: Re-enable layout transitions for addition of the unpinned task
@ -1041,13 +1040,13 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
/** /**
* Called when a gesture from an app is starting. * Called when a gesture from an app is starting.
*/ */
public void onGestureAnimationStart(int runningTaskId) { public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
// This needs to be called before the other states are set since it can create the task view // This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) { if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler(); updateOrientationHandler();
} }
showCurrentTask(runningTaskId); showCurrentTask(runningTaskInfo);
setEnableFreeScroll(false); setEnableFreeScroll(false);
setEnableDrawingLiveTile(false); setEnableDrawingLiveTile(false);
setRunningTaskHidden(true); setRunningTaskHidden(true);
@ -1078,7 +1077,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
pa.addListener(AnimationSuccessListener.forRunnable(() -> { pa.addListener(AnimationSuccessListener.forRunnable(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation()); setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
mActivity.getDragLayer().recreateControllers(); mActivity.getDragLayer().recreateControllers();
rotateAllChildTasks(); updateChildTaskOrientations();
setRecentsChangedOrientation(false).start(); setRecentsChangedOrientation(false).start();
})); }));
pa.start(); pa.start();
@ -1099,7 +1098,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
} }
private void rotateAllChildTasks() { private void updateChildTaskOrientations() {
for (int i = 0; i < getTaskViewCount(); i++) { for (int i = 0; i < getTaskViewCount(); i++) {
getTaskViewAt(i).setOrientationState(mOrientationState); getTaskViewAt(i).setOrientationState(mOrientationState);
} }
@ -1127,8 +1126,8 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
/** /**
* Returns true if we should add a dummy taskView for the running task id * Returns true if we should add a dummy taskView for the running task id
*/ */
protected boolean shouldAddDummyTaskView(int runningTaskId) { protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
return getTaskView(runningTaskId) == null; return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
} }
/** /**
@ -1137,8 +1136,8 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
* All subsequent calls to reload will keep the task as the first item until {@link #reset()} * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task. * is called. Also scrolls the view to this task.
*/ */
public void showCurrentTask(int runningTaskId) { public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
if (shouldAddDummyTaskView(runningTaskId)) { if (shouldAddDummyTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0; boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied // Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView(); final TaskView taskView = mTaskViewPool.getView();
@ -1148,10 +1147,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
} }
// The temporary running task is only used for the duration between the start of the // The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied // gesture and the task list is loaded and applied
mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(), mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
false, true, false, false, new ActivityManager.TaskDescription(), 0,
new ComponentName("", ""), false);
taskView.bind(mTmpRunningTask, mOrientationState); taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly // Measure and layout immediately so that the scroll values is updated instantly
@ -1162,7 +1158,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
} }
boolean runningTaskTileHidden = mRunningTaskTileHidden; boolean runningTaskTileHidden = mRunningTaskTileHidden;
setCurrentTask(runningTaskId); setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
setCurrentPage(getRunningTaskIndex()); setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false); setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden); setRunningTaskHidden(runningTaskTileHidden);
@ -1652,6 +1648,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
super.setVisibility(visibility); super.setVisibility(visibility);
if (mActionsView != null) { if (mActionsView != null) {
mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE); mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
if (visibility != VISIBLE) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
} }
} }
@ -1680,10 +1679,11 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
: View.LAYOUT_DIRECTION_RTL); : View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated()); mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers(); mActivity.getDragLayer().recreateControllers();
boolean isInLandscape = mOrientationState.getTouchRotation() != 0 boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0; || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
!mOrientationState.canRecentsActivityRotate() && isInLandscape); !mOrientationState.canRecentsActivityRotate() && isInLandscape);
updateChildTaskOrientations();
resetPaddingFromTaskSize(); resetPaddingFromTaskSize();
requestLayout(); requestLayout();
// Reapply the current page to update page scrolls. // Reapply the current page to update page scrolls.
@ -1998,19 +1998,12 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) { protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
} }
public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
protected void onTaskLaunchAnimationEnd(boolean success) { protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) { if (success) {
resetTaskVisuals(); resetTaskVisuals();
} }
} }
/**
* Called when task activity is launched
*/
public void onTaskLaunched(Task task){ }
@Override @Override
protected void notifyPageSwitchListener(int prevPage) { protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage); super.notifyPageSwitchListener(prevPage);

View File

@ -169,7 +169,9 @@ public class TaskMenuView extends AbstractFloatingView {
} }
if (mIsOpen) { if (mIsOpen) {
mOptionLayout.removeAllViews(); mOptionLayout.removeAllViews();
populateAndLayoutMenu(); if (!populateAndLayoutMenu()) {
close(false);
}
} }
} }
@ -186,14 +188,22 @@ public class TaskMenuView extends AbstractFloatingView {
} }
mActivity.getDragLayer().addView(this); mActivity.getDragLayer().addView(this);
mTaskView = taskView; mTaskView = taskView;
populateAndLayoutMenu(); if (!populateAndLayoutMenu()) {
return false;
}
post(this::animateOpen); post(this::animateOpen);
return true; return true;
} }
private void populateAndLayoutMenu() { /** @return true if successfully able to populate task view menu, false otherwise */
private boolean populateAndLayoutMenu() {
if (mTaskView.getTask().icon == null) {
// Icon may not be loaded
return false;
}
addMenuOptions(mTaskView); addMenuOptions(mTaskView);
orientAroundTaskView(mTaskView); orientAroundTaskView(mTaskView);
return true;
} }
private void addMenuOptions(TaskView taskView) { private void addMenuOptions(TaskView taskView) {
@ -240,8 +250,10 @@ public class TaskMenuView extends AbstractFloatingView {
setLayoutParams(params); setLayoutParams(params);
setScaleX(taskView.getScaleX()); setScaleX(taskView.getScaleX());
setScaleY(taskView.getScaleY()); setScaleY(taskView.getScaleY());
boolean canActivityRotate = taskView.getRecentsView()
.mOrientationState.canRecentsActivityRotate();
mOptionLayout.setOrientation(orientationHandler mOptionLayout.setOrientation(orientationHandler
.getTaskMenuLayoutOrientation(mOptionLayout)); .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
taskView.getPagedOrientationHandler()); taskView.getPagedOrientationHandler());
} }

View File

@ -357,7 +357,7 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
} }
private void updateOverlay() { private void updateOverlay() {
if (mOverlayEnabled && mBitmapShader != null && mThumbnailData != null) { if (mOverlayEnabled) {
mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix, mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
mPreviewPositionHelper.mIsOrientationChanged); mPreviewPositionHelper.mIsOrientationChanged);
} else { } else {

View File

@ -30,8 +30,7 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import android.animation.Animator; import android.animation.Animator;
@ -385,7 +384,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
} }
}, resultCallbackHandler); }, resultCallbackHandler);
} }
getRecentsView().onTaskLaunched(mTask);
} }
} }

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FFFFFFFF" />
</shape>

View File

@ -24,6 +24,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/> android:background="@drawable/gesture_tutorial_ripple"/>
<com.android.launcher3.views.ClipIconView
android:id="@+id/gesture_tutorial_fake_icon_view"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/bg_circle"
android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
android:visibility="invisible" />
<View <View
android:id="@+id/gesture_tutorial_fake_task_view" android:id="@+id/gesture_tutorial_fake_task_view"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -41,81 +49,81 @@
android:id="@+id/gesture_tutorial_fragment_close_button" android:id="@+id/gesture_tutorial_fragment_close_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="18dp"
android:layout_marginTop="30dp"
android:layout_marginStart="4dp"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:background="@android:color/transparent" android:layout_marginStart="4dp"
android:layout_marginTop="30dp"
android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container" android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container"
android:background="@android:color/transparent"
android:contentDescription="@string/gesture_tutorial_close_button_content_description" android:contentDescription="@string/gesture_tutorial_close_button_content_description"
android:tint="?android:attr/textColorPrimary" android:padding="18dp"
android:src="@drawable/gesture_tutorial_close_button"/> android:src="@drawable/gesture_tutorial_close_button"
android:tint="?android:attr/textColorPrimary"/>
<LinearLayout <LinearLayout
android:id="@+id/gesture_tutorial_fragment_titles_container" android:id="@+id/gesture_tutorial_fragment_titles_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="70dp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginTop="70dp"
android:focusable="true" android:focusable="true"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/gesture_tutorial_fragment_title_view" android:id="@+id/gesture_tutorial_fragment_title_view"
style="@style/TextAppearance.GestureTutorial.Title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end" android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end" android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
style="@style/TextAppearance.GestureTutorial.Title"/>
<TextView <TextView
android:id="@+id/gesture_tutorial_fragment_subtitle_view" android:id="@+id/gesture_tutorial_fragment_subtitle_view"
style="@style/TextAppearance.GestureTutorial.Subtitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end" android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end" android:layout_marginTop="10dp"
style="@style/TextAppearance.GestureTutorial.Subtitle"/> android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/gesture_tutorial_fragment_feedback_view" android:id="@+id/gesture_tutorial_fragment_feedback_view"
style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_centerHorizontal="true"
android:layout_above="@id/gesture_tutorial_fragment_action_button" android:layout_above="@id/gesture_tutorial_fragment_action_button"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end" android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end" android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
style="@style/TextAppearance.GestureTutorial.Feedback"/> android:layout_marginBottom="10dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase <!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground --> of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button <Button
android:id="@+id/gesture_tutorial_fragment_action_button" android:id="@+id/gesture_tutorial_fragment_action_button"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="142dp" android:layout_width="142dp"
android:layout_height="49dp" android:layout_height="49dp"
android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:stateListAnimator="@null" android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:background="@drawable/gesture_tutorial_action_button_background" android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"/> android:stateListAnimator="@null"/>
<Button <Button
android:id="@+id/gesture_tutorial_fragment_action_text_button" android:id="@+id/gesture_tutorial_fragment_action_text_button"
style="@style/TextAppearance.GestureTutorial.TextButtonLabel"
android:layout_width="142dp" android:layout_width="142dp"
android:layout_height="49dp" android:layout_height="49dp"
android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:stateListAnimator="@null" android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:background="@null" android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
style="@style/TextAppearance.GestureTutorial.TextButtonLabel"/> android:stateListAnimator="@null"/>
</RelativeLayout> </RelativeLayout>

View File

@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.LauncherUIHelper.doLayout; import static com.android.launcher3.util.LauncherUIHelper.doLayout;
import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Bitmap.Config; import android.graphics.Bitmap.Config;
@ -50,7 +51,7 @@ public class RecentsActivityTest {
} }
@Test @Test
public void testRecets_showCurrentTask() { public void testRecents_showCurrentTask() {
ActivityController<RecentsActivity> controller = ActivityController<RecentsActivity> controller =
Robolectric.buildActivity(RecentsActivity.class); Robolectric.buildActivity(RecentsActivity.class);
@ -58,7 +59,10 @@ public class RecentsActivityTest {
doLayout(activity); doLayout(activity);
FallbackRecentsView frv = activity.getOverviewPanel(); FallbackRecentsView frv = activity.getOverviewPanel();
frv.showCurrentTask(22);
RunningTaskInfo dummyTask = new RunningTaskInfo();
dummyTask.taskId = 22;
frv.showCurrentTask(dummyTask);
doLayout(activity); doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData(); ThumbnailData thumbnailData = new ThumbnailData();

View File

@ -157,6 +157,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override @Override
protected void onDeferredResumed() { protected void onDeferredResumed() {
super.onDeferredResumed(); super.onDeferredResumed();
handlePendingActivityRequest();
}
@Override
protected void handlePendingActivityRequest() {
super.handlePendingActivityRequest();
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) { if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher. // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null); onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);

View File

@ -16,8 +16,6 @@
package com.android.launcher3; package com.android.launcher3;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL; import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@ -62,7 +60,6 @@ import android.os.CancellationSignal;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Pair; import android.util.Pair;
import android.util.TypedValue;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -879,10 +876,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
} }
}); });
} else { } else {
float velocityDpPerS = DynamicResource.provider(mLauncher) float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false) anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
.getAnimators()); .getAnimators());
} }

View File

@ -150,16 +150,9 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return deviceState.isInDeferredGestureRegion(ev); return deviceState.isInDeferredGestureRegion(ev);
} }
public abstract void onExitOverview(RecentsAnimationDeviceState deviceState, public abstract void onExitOverview(RotationTouchHelper deviceState,
Runnable exitRunnable); Runnable exitRunnable);
/**
* Updates the prediction state to the overview state.
*/
public void updateOverviewPredictionState() {
// By public overview predictions are not supported
}
/** /**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher} * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/ */

View File

@ -16,11 +16,9 @@
package com.android.quickstep; package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL; import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY; import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@ -49,23 +47,19 @@ import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.OrientationEventListener;
import androidx.annotation.BinderThread; import androidx.annotation.BinderThread;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay; import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.SecureSettingsObserver; import com.android.launcher3.util.SecureSettingsObserver;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
@ -83,7 +77,7 @@ public class RecentsAnimationDeviceState implements
private final SysUINavigationMode mSysUiNavMode; private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay; private final DefaultDisplay mDefaultDisplay;
private final int mDisplayId; private final int mDisplayId;
private int mDisplayRotation; private final RotationTouchHelper mRotationTouchHelper;
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>(); private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
@ -107,76 +101,10 @@ public class RecentsAnimationDeviceState implements
} }
}; };
private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
mTaskListFrozen = frozen;
if (frozen || mInOverview) {
return;
}
enableMultipleRegions(false);
}
@Override
public void onActivityRotation(int displayId) {
super.onActivityRotation(displayId);
// This always gets called before onDisplayInfoChanged() so we know how to process
// the rotation in that method. This is done to avoid having a race condition between
// the sensor readings and onDisplayInfoChanged() call
if (displayId != mDisplayId) {
return;
}
mPrioritizeDeviceRotation = true;
if (mInOverview) {
// reset, launcher must be rotating
mExitOverviewRunnable.run();
}
}
};
private Runnable mExitOverviewRunnable = new Runnable() {
@Override
public void run() {
mInOverview = false;
enableMultipleRegions(false);
}
};
private OrientationTouchTransformer mOrientationTouchTransformer;
/**
* Used to listen for when the device rotates into the orientation of the current
* foreground app. For example, if a user quickswitches from a portrait to a fixed landscape
* app and then rotates rotates the device to match that orientation, this triggers calls to
* sysui to adjust the navbar.
*/
private OrientationEventListener mOrientationListener;
private int mSensorRotation = ROTATION_0;
/**
* This is the configuration of the foreground app or the app that will be in the foreground
* once a quickstep gesture finishes.
*/
private int mCurrentAppRotation = -1;
/**
* This flag is set to true when the device physically changes orientations. When true,
* we will always report the current rotation of the foreground app whenever the display
* changes, as it would indicate the user's intention to rotate the foreground app.
*/
private boolean mPrioritizeDeviceRotation = false;
private Region mExclusionRegion; private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener; private SystemGestureExclusionListenerCompat mExclusionListener;
private final List<ComponentName> mGestureBlockedActivities; private final List<ComponentName> mGestureBlockedActivities;
private Runnable mOnDestroyFrozenTaskRunnable;
/**
* Set to true when user swipes to recents. In recents, we ignore the state of the recents
* task list being frozen or not to allow the user to keep interacting with nav bar rotation
* they went into recents with as opposed to defaulting to the default display rotation.
* TODO: (b/156984037) For when user rotates after entering overview
*/
private boolean mInOverview;
private boolean mTaskListFrozen;
private boolean mIsUserSetupComplete; private boolean mIsUserSetupComplete;
@ -186,6 +114,8 @@ public class RecentsAnimationDeviceState implements
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context); mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
mDisplayId = mDefaultDisplay.getInfo().id; mDisplayId = mDefaultDisplay.getInfo().id;
runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this)); runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
runOnDestroy(mRotationTouchHelper::destroy);
// Register for user unlocked if necessary // Register for user unlocked if necessary
mIsUserUnlocked = context.getSystemService(UserManager.class) mIsUserUnlocked = context.getSystemService(UserManager.class)
@ -207,10 +137,6 @@ public class RecentsAnimationDeviceState implements
}; };
runOnDestroy(mExclusionListener::unregister); runOnDestroy(mExclusionListener::unregister);
Resources resources = mContext.getResources();
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
() -> QuickStepContract.getWindowCornerRadius(resources));
// Register for navigation mode changes // Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this)); onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this)); runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
@ -241,38 +167,6 @@ public class RecentsAnimationDeviceState implements
userSetupObserver.register(); userSetupObserver.register();
runOnDestroy(userSetupObserver::unregister); runOnDestroy(userSetupObserver::unregister);
} }
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int degrees) {
int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
mSensorRotation);
if (newRotation == mSensorRotation) {
return;
}
mSensorRotation = newRotation;
mPrioritizeDeviceRotation = true;
if (newRotation == mCurrentAppRotation) {
// When user rotates device to the orientation of the foreground app after
// quickstepping
toggleSecondaryNavBarsForRotation();
}
}
};
}
private void setupOrientationSwipeHandler() {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener);
runOnDestroy(mOnDestroyFrozenTaskRunnable);
}
private void destroyOrientationSwipeHandlerCallback() {
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
} }
private void runOnDestroy(Runnable action) { private void runOnDestroy(Runnable action) {
@ -311,13 +205,6 @@ public class RecentsAnimationDeviceState implements
mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo()); mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo());
mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
if (!mMode.hasGestures && newMode.hasGestures) {
setupOrientationSwipeHandler();
} else if (mMode.hasGestures && !newMode.hasGestures){
destroyOrientationSwipeHandlerCallback();
}
mMode = newMode; mMode = newMode;
} }
@ -328,28 +215,10 @@ public class RecentsAnimationDeviceState implements
return; return;
} }
mDisplayRotation = info.rotation;
if (!mMode.hasGestures) { if (!mMode.hasGestures) {
return; return;
} }
mNavBarPosition = new NavBarPosition(mMode, info); mNavBarPosition = new NavBarPosition(mMode, info);
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
mCurrentAppRotation = mDisplayRotation;
/* Update nav bars on the following:
* a) if this is coming from an activity rotation OR
* aa) we launch an app in the orientation that user is already in
* b) We're not in overview, since overview will always be portrait (w/o home rotation)
* c) We're actively in quickswitch mode
*/
if ((mPrioritizeDeviceRotation
|| mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
&& !mInOverview
&& mTaskListFrozen) {
toggleSecondaryNavBarsForRotation();
}
} }
/** /**
@ -464,7 +333,7 @@ public class RecentsAnimationDeviceState implements
*/ */
public boolean canStartSystemGesture() { public boolean canStartSystemGesture() {
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
|| mTaskListFrozen; || mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
@ -536,33 +405,6 @@ public class RecentsAnimationDeviceState implements
return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0; return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
} }
/**
* Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
*/
public void updateGestureTouchRegions() {
if (!mMode.hasGestures) {
return;
}
mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
}
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event) {
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
}
/**
* @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
* is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
event.getY(pointerIndex));
}
/** /**
* Sets the region in screen space where the gestures should be deferred (ie. due to specific * Sets the region in screen space where the gestures should be deferred (ie. due to specific
* nav bar ui). * nav bar ui).
@ -620,101 +462,13 @@ public class RecentsAnimationDeviceState implements
public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) { public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
return mAssistantAvailable return mAssistantAvailable
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
&& mOrientationTouchTransformer.touchInAssistantRegion(ev) && mRotationTouchHelper.touchInAssistantRegion(ev)
&& !isLockToAppActive() && !isLockToAppActive()
&& !isGestureBlockedActivity(task); && !isGestureBlockedActivity(task);
} }
/** public RotationTouchHelper getRotationTouchHelper() {
* *May* apply a transform on the motion event if it lies in the nav bar region for another return mRotationTouchHelper;
* orientation that is currently being tracked as a part of quickstep
*/
void setOrientationTransformIfNeeded(MotionEvent event) {
// negative coordinates bug b/143901881
if (event.getX() < 0 || event.getY() < 0) {
event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
}
mOrientationTouchTransformer.transform(event);
}
private void enableMultipleRegions(boolean enable) {
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
// Clear any previous state from sensor manager
mSensorRotation = mCurrentAppRotation;
mOrientationListener.enable();
} else {
mOrientationListener.disable();
}
}
public void onStartGesture() {
if (mTaskListFrozen) {
// Prioritize whatever nav bar user touches once in quickstep
// This case is specifically when user changes what nav bar they are using mid
// quickswitch session before tasks list is unfrozen
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
}
void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
BaseActivityInterface activityInterface) {
if (endTarget == GestureState.GestureEndTarget.RECENTS) {
mInOverview = true;
if (!mTaskListFrozen) {
// If we're in landscape w/o ever quickswitching, show the navbar in landscape
enableMultipleRegions(true);
}
activityInterface.onExitOverview(this, mExitOverviewRunnable);
} else if (endTarget == GestureState.GestureEndTarget.HOME) {
enableMultipleRegions(false);
} else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
// First gesture to start quickswitch
enableMultipleRegions(true);
} else {
notifySysuiOfCurrentRotation(
mOrientationTouchTransformer.getCurrentActiveRotation());
}
// A new gesture is starting, reset the current device rotation
// This is done under the assumption that the user won't rotate the phone and then
// quickswitch in the old orientation.
mPrioritizeDeviceRotation = false;
} else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
if (!mTaskListFrozen) {
// touched nav bar but didn't go anywhere and not quickswitching, do nothing
return;
}
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
}
private void notifySysuiOfCurrentRotation(int rotation) {
UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
.onQuickSwitchToNewTask(rotation));
}
/**
* Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
* notifies system UI of the primary rotation the user is interacting with
*/
private void toggleSecondaryNavBarsForRotation() {
mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
public int getCurrentActiveRotation() {
if (!mMode.hasGestures) {
// touch rotation should always match that of display for 3 button
return mDisplayRotation;
}
return mOrientationTouchTransformer.getCurrentActiveRotation();
}
public int getDisplayRotation() {
return mDisplayRotation;
} }
public void dump(PrintWriter pw) { public void dump(PrintWriter pw) {
@ -726,9 +480,7 @@ public class RecentsAnimationDeviceState implements
pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled=" pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
pw.println(" displayRotation=" + getDisplayRotation());
pw.println(" isUserUnlocked=" + mIsUserUnlocked); pw.println(" isUserUnlocked=" + mIsUserUnlocked);
mOrientationTouchTransformer.dump(pw); mRotationTouchHelper.dump(pw);
} }
} }

View File

@ -92,15 +92,6 @@ public class RecentsModel extends TaskStackChangeListener {
return mTaskList.getTasks(false /* loadKeysOnly */, callback); return mTaskList.getTasks(false /* loadKeysOnly */, callback);
} }
/**
* @return The task id of the running task, or -1 if there is no current running task.
*/
public static int getRunningTaskId() {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
return runningTask != null ? runningTask.id : -1;
}
/** /**
* @return Whether the provided {@param changeId} is the latest recent tasks list id. * @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/ */
@ -140,7 +131,9 @@ public class RecentsModel extends TaskStackChangeListener {
} }
// Keep the cache up to date with the latest thumbnails // Keep the cache up to date with the latest thumbnails
int runningTaskId = RecentsModel.getRunningTaskId(); ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
int runningTaskId = runningTask != null ? runningTask.id : -1;
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> { mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) { for (Task task : tasks) {
if (task.key.id == runningTaskId) { if (task.key.id == runningTaskId) {

View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2020 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 static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import android.content.Context;
import android.content.res.Resources;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
public class RotationTouchHelper implements
SysUINavigationMode.NavigationModeChangeListener,
DefaultDisplay.DisplayInfoChangeListener {
public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
new MainThreadInitializedObject<>(RotationTouchHelper::new);
private final OrientationTouchTransformer mOrientationTouchTransformer;
private final DefaultDisplay mDefaultDisplay;
private final SysUINavigationMode mSysUiNavMode;
private final int mDisplayId;
private int mDisplayRotation;
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
mTaskListFrozen = frozen;
if (frozen || mInOverview) {
return;
}
enableMultipleRegions(false);
}
@Override
public void onActivityRotation(int displayId) {
super.onActivityRotation(displayId);
// This always gets called before onDisplayInfoChanged() so we know how to process
// the rotation in that method. This is done to avoid having a race condition between
// the sensor readings and onDisplayInfoChanged() call
if (displayId != mDisplayId) {
return;
}
mPrioritizeDeviceRotation = true;
if (mInOverview) {
// reset, launcher must be rotating
mExitOverviewRunnable.run();
}
}
};
private Runnable mExitOverviewRunnable = new Runnable() {
@Override
public void run() {
mInOverview = false;
enableMultipleRegions(false);
}
};
/**
* Used to listen for when the device rotates into the orientation of the current foreground
* app. For example, if a user quickswitches from a portrait to a fixed landscape app and then
* rotates rotates the device to match that orientation, this triggers calls to sysui to adjust
* the navbar.
*/
private OrientationEventListener mOrientationListener;
private int mSensorRotation = ROTATION_0;
/**
* This is the configuration of the foreground app or the app that will be in the foreground
* once a quickstep gesture finishes.
*/
private int mCurrentAppRotation = -1;
/**
* This flag is set to true when the device physically changes orientations. When true, we will
* always report the current rotation of the foreground app whenever the display changes, as it
* would indicate the user's intention to rotate the foreground app.
*/
private boolean mPrioritizeDeviceRotation = false;
private Runnable mOnDestroyFrozenTaskRunnable;
/**
* Set to true when user swipes to recents. In recents, we ignore the state of the recents
* task list being frozen or not to allow the user to keep interacting with nav bar rotation
* they went into recents with as opposed to defaulting to the default display rotation.
* TODO: (b/156984037) For when user rotates after entering overview
*/
private boolean mInOverview;
private boolean mTaskListFrozen;
private final Context mContext;
private RotationTouchHelper(Context context) {
mContext = context;
Resources resources = mContext.getResources();
mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
mDisplayId = mDefaultDisplay.getInfo().id;
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
() -> QuickStepContract.getWindowCornerRadius(resources));
// Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int degrees) {
int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
mSensorRotation);
if (newRotation == mSensorRotation) {
return;
}
mSensorRotation = newRotation;
mPrioritizeDeviceRotation = true;
if (newRotation == mCurrentAppRotation) {
// When user rotates device to the orientation of the foreground app after
// quickstepping
toggleSecondaryNavBarsForRotation();
}
}
};
}
private void setupOrientationSwipeHandler() {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener);
runOnDestroy(mOnDestroyFrozenTaskRunnable);
}
private void destroyOrientationSwipeHandlerCallback() {
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
}
private void runOnDestroy(Runnable action) {
mOnDestroyActions.add(action);
}
/**
* Cleans up all the registered listeners and receivers.
*/
public void destroy() {
for (Runnable r : mOnDestroyActions) {
r.run();
}
}
public boolean isTaskListFrozen() {
return mTaskListFrozen;
}
public boolean touchInAssistantRegion(MotionEvent ev) {
return mOrientationTouchTransformer.touchInAssistantRegion(ev);
}
/**
* Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
*/
public void updateGestureTouchRegions() {
if (!mMode.hasGestures) {
return;
}
mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
}
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event) {
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
}
/**
* @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
* is in the swipe up gesture region.
*/
public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
event.getY(pointerIndex));
}
@Override
public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
mDefaultDisplay.removeChangeListener(this);
mDefaultDisplay.addChangeListener(this);
onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
if (!mMode.hasGestures && newMode.hasGestures) {
setupOrientationSwipeHandler();
} else if (mMode.hasGestures && !newMode.hasGestures){
destroyOrientationSwipeHandlerCallback();
}
mMode = newMode;
}
public int getDisplayRotation() {
return mDisplayRotation;
}
@Override
public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
// ignore displays that aren't running launcher and frame refresh rate changes
return;
}
mDisplayRotation = info.rotation;
if (!mMode.hasGestures) {
return;
}
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
mCurrentAppRotation = mDisplayRotation;
/* Update nav bars on the following:
* a) if this is coming from an activity rotation OR
* aa) we launch an app in the orientation that user is already in
* b) We're not in overview, since overview will always be portrait (w/o home rotation)
* c) We're actively in quickswitch mode
*/
if ((mPrioritizeDeviceRotation
|| mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
&& !mInOverview
&& mTaskListFrozen) {
toggleSecondaryNavBarsForRotation();
}
}
/**
* *May* apply a transform on the motion event if it lies in the nav bar region for another
* orientation that is currently being tracked as a part of quickstep
*/
void setOrientationTransformIfNeeded(MotionEvent event) {
// negative coordinates bug b/143901881
if (event.getX() < 0 || event.getY() < 0) {
event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
}
mOrientationTouchTransformer.transform(event);
}
private void enableMultipleRegions(boolean enable) {
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
// Clear any previous state from sensor manager
mSensorRotation = mCurrentAppRotation;
mOrientationListener.enable();
} else {
mOrientationListener.disable();
}
}
public void onStartGesture() {
if (mTaskListFrozen) {
// Prioritize whatever nav bar user touches once in quickstep
// This case is specifically when user changes what nav bar they are using mid
// quickswitch session before tasks list is unfrozen
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
}
void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
BaseActivityInterface activityInterface) {
if (endTarget == GestureState.GestureEndTarget.RECENTS) {
mInOverview = true;
if (!mTaskListFrozen) {
// If we're in landscape w/o ever quickswitching, show the navbar in landscape
enableMultipleRegions(true);
}
activityInterface.onExitOverview(this, mExitOverviewRunnable);
} else if (endTarget == GestureState.GestureEndTarget.HOME) {
enableMultipleRegions(false);
} else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
// First gesture to start quickswitch
enableMultipleRegions(true);
} else {
notifySysuiOfCurrentRotation(
mOrientationTouchTransformer.getCurrentActiveRotation());
}
// A new gesture is starting, reset the current device rotation
// This is done under the assumption that the user won't rotate the phone and then
// quickswitch in the old orientation.
mPrioritizeDeviceRotation = false;
} else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
if (!mTaskListFrozen) {
// touched nav bar but didn't go anywhere and not quickswitching, do nothing
return;
}
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
}
private void notifySysuiOfCurrentRotation(int rotation) {
UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
.onQuickSwitchToNewTask(rotation));
}
/**
* Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
* notifies system UI of the primary rotation the user is interacting with
*/
private void toggleSecondaryNavBarsForRotation() {
mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
public int getCurrentActiveRotation() {
if (!mMode.hasGestures) {
// touch rotation should always match that of display for 3 button
return mDisplayRotation;
}
return mOrientationTouchTransformer.getCurrentActiveRotation();
}
public void dump(PrintWriter pw) {
pw.println("RotationTouchHelper:");
pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
pw.println(" displayRotation=" + getDisplayRotation());
mOrientationTouchTransformer.dump(pw);
}
}

View File

@ -17,6 +17,7 @@ package com.android.quickstep.interaction;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
@ -110,6 +111,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation, boolean isReverse) { public void onAnimationEnd(Animator animation, boolean isReverse) {
mFakeIconView.setVisibility(View.INVISIBLE);
mFakeTaskView.setVisibility(View.INVISIBLE); mFakeTaskView.setVisibility(View.INVISIBLE);
mFakeTaskView.setAlpha(1); mFakeTaskView.setAlpha(1);
mRunningWindowAnim = null; mRunningWindowAnim = null;
@ -131,6 +133,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
}); });
} else { } else {
anim.setViewAlpha(mFakeTaskView, 0, ACCEL); anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
anim.setViewAlpha(mFakeIconView, 0, ACCEL);
anim.addListener(resetTaskView); anim.addListener(resetTaskView);
} }
if (onEndRunnable != null) { if (onEndRunnable != null) {
@ -202,7 +205,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
// derivative of the scroll interpolator at zero, ie. 2. // derivative of the scroll interpolator at zero, ie. 2.
long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory() {
@Override @Override
public AnimatorPlaybackController createActivityAnimationToHome() { public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@ -218,6 +221,24 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
fakeHomeIconLeft + fakeHomeIconSizePx, fakeHomeIconLeft + fakeHomeIconSizePx,
fakeHomeIconTop + fakeHomeIconSizePx); fakeHomeIconTop + fakeHomeIconSizePx);
} }
@Override
public void update(RectF rect, float progress, float radius) {
mFakeIconView.setVisibility(View.VISIBLE);
mFakeIconView.update(rect, progress,
1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
radius,
false, /* isOpening */
mFakeIconView, mDp,
false /* isVerticalBarLayout */);
mFakeIconView.setAlpha(1);
mFakeTaskView.setAlpha(getWindowAlpha(progress));
}
@Override
public void onCancel() {
mFakeIconView.setVisibility(View.INVISIBLE);
}
}; };
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
windowAnim.start(mContext, velocityPxPerMs); windowAnim.start(mContext, velocityPxPerMs);

View File

@ -28,6 +28,7 @@ import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@ -46,6 +47,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final TextView mTitleTextView; final TextView mTitleTextView;
final TextView mSubtitleTextView; final TextView mSubtitleTextView;
final TextView mFeedbackView; final TextView mFeedbackView;
final ClipIconView mFakeIconView;
final View mFakeTaskView; final View mFakeTaskView;
final View mRippleView; final View mRippleView;
final RippleDrawable mRippleDrawable; final RippleDrawable mRippleDrawable;
@ -66,6 +68,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view); mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view); mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view); mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view); mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view); mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground(); mRippleDrawable = (RippleDrawable) mRippleView.getBackground();

View File

@ -27,7 +27,7 @@ import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGE
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
@ -54,6 +54,7 @@ import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* This class calls StatsLog compile time generated methods. * This class calls StatsLog compile time generated methods.
@ -68,8 +69,6 @@ public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog"; private static final String TAG = "StatsLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG); private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
private static Context sContext;
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0); private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure. // from nano to lite, bake constant to prevent robo test failure.
@ -77,8 +76,13 @@ public class StatsLogCompatManager extends StatsLogManager {
private static final int FOLDER_HIERARCHY_OFFSET = 100; private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200; private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
new CopyOnWriteArrayList<>();
private final Context mContext;
public StatsLogCompatManager(Context context) { public StatsLogCompatManager(Context context) {
sContext = context; mContext = context;
} }
@Override @Override
@ -86,25 +90,12 @@ public class StatsLogCompatManager extends StatsLogManager {
return new StatsCompatLogger(); return new StatsCompatLogger();
} }
/**
* Logs a ranking event and accompanying {@link InstanceId} and package name.
*/
@Override
public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
int position) {
SysUiStatsLog.write(SysUiStatsLog.RANKING_SELECTED,
rankingEvent.getId() /* event_id = 1; */,
packageName /* package_name = 2; */,
instanceId.getId() /* instance_id = 3; */,
position /* position_picked = 4; */);
}
/** /**
* Logs the workspace layout information on the model thread. * Logs the workspace layout information on the model thread.
*/ */
@Override @Override
public void logSnapshot() { public void logSnapshot() {
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask( LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
new SnapshotWorker()); new SnapshotWorker());
} }
@ -175,6 +166,7 @@ public class StatsLogCompatManager extends StatsLogManager {
private static class StatsCompatLogger implements StatsLogger { private static class StatsCompatLogger implements StatsLogger {
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo(); private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO; private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID; private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty(); private OptionalInt mRank = OptionalInt.empty();
@ -253,36 +245,35 @@ public class StatsLogCompatManager extends StatsLogManager {
return; return;
} }
if (mItemInfo.container < 0) { LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
// Item is not within a folder. Write to StatsLog in same thread. if (mItemInfo.container < 0 || appState == null) {
write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState, // Write log on the model thread so that logs do not go out of order
mDstState); // (for eg: drop comes after drag)
Executors.MODEL_EXECUTOR.execute(
() -> write(event, applyOverwrites(mItemInfo.buildProto())));
} else { } else {
// Item is inside the folder, fetch folder info in a BG thread // Item is inside the folder, fetch folder info in a BG thread
// and then write to StatsLog. // and then write to StatsLog.
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask( appState.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() { new BaseModelUpdateTask() {
@Override @Override
public void execute(LauncherAppState app, BgDataModel dataModel, public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) { AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container); FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
write(event, mInstanceId, write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
applyOverwrites(mItemInfo.buildProto(folderInfo)),
mSrcState, mDstState);
} }
}); });
} }
} }
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) { private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
LauncherAtom.ItemInfo.Builder itemInfoBuilder = LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
(LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
mRank.ifPresent(itemInfoBuilder::setRank); mRank.ifPresent(itemInfoBuilder::setRank);
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo); mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) { if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder FolderIcon.Builder folderIconBuilder = itemInfoBuilder
.getFolderIcon() .getFolderIcon()
.toBuilder(); .toBuilder();
mFromState.ifPresent(folderIconBuilder::setFromLabelState); mFromState.ifPresent(folderIconBuilder::setFromLabelState);
@ -293,8 +284,11 @@ public class StatsLogCompatManager extends StatsLogManager {
return itemInfoBuilder.build(); return itemInfoBuilder.build();
} }
private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo, @WorkerThread
int srcState, int dstState) { private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
InstanceId instanceId = mInstanceId;
int srcState = mSrcState;
int dstState = mDstState;
if (IS_VERBOSE) { if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() : String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + ""; event.getId() + "";
@ -307,6 +301,10 @@ public class StatsLogCompatManager extends StatsLogManager {
atomInfo)); atomInfo));
} }
for (StatsLogConsumer consumer : LOGS_CONSUMER) {
consumer.consume(event, atomInfo);
}
SysUiStatsLog.write( SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT, SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */, SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@ -446,7 +444,16 @@ public class StatsLogCompatManager extends StatsLogManager {
return "ALLAPPS"; return "ALLAPPS";
default: default:
return "INVALID"; return "INVALID";
} }
} }
/**
* Interface to get stats log while it is dispatched to the system
*/
public interface StatsLogConsumer {
@WorkerThread
void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
}
} }

View File

@ -49,7 +49,7 @@ public class LayoutUtils {
Rect taskSize = new Rect(); Rect taskSize = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize, LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
orientationHandler); orientationHandler);
return (dp.heightPx - taskSize.height()) / 2; return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
} }
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension( int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(

View File

@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto" xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:focusable="false">
<com.android.launcher3.CellLayout <com.android.launcher3.CellLayout
android:id="@+id/workspace" android:id="@+id/workspace"

View File

@ -142,7 +142,7 @@
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item> <item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item> <item name="staggered_stiffness" type="dimen" format="float">150</item>
<dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen> <dimen name="unlock_staggered_velocity_dp_per_s">4dp</dimen>
<item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item> <item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
<item name="hint_scale_stiffness" type="dimen" format="float">200</item> <item name="hint_scale_stiffness" type="dimen" format="float">200</item>

View File

@ -45,7 +45,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemClickHandler;
@ -82,7 +81,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode", mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode()); () -> getPackageManager().isSafeMode());
DefaultDisplay.INSTANCE.get(this).addChangeListener(this); DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
@ -154,8 +153,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
public abstract ActivityOptions getActivityLaunchOptions(View v); public abstract ActivityOptions getActivityLaunchOptions(View v);
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item, public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
@Nullable String sourceContainer) {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false; return false;
@ -176,17 +174,13 @@ public abstract class BaseDraggingActivity extends BaseActivity
&& !((WorkspaceItemInfo) item).isPromise(); && !((WorkspaceItemInfo) item).isPromise();
if (isShortcut) { if (isShortcut) {
// Shortcuts need some special checks due to legacy reasons. // Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer); startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) { } else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity // Could be launching some bookkeeping activity
startActivity(intent, optsBundle); startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
} else { } else {
getSystemService(LauncherApps.class).startMainActivity( getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle); intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
sourceContainer);
} }
getUserEventDispatcher().logAppLaunch(v, intent, user); getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) { if (item != null) {
@ -206,8 +200,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
.log(LAUNCHER_APP_LAUNCH_TAP); .log(LAUNCHER_APP_LAUNCH_TAP);
} }
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info, private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
@Nullable String sourceContainer) {
try { try {
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
try { try {
@ -221,8 +214,6 @@ public abstract class BaseDraggingActivity extends BaseActivity
String id = ((WorkspaceItemInfo) info).getDeepShortcutId(); String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
String packageName = intent.getPackage(); String packageName = intent.getPackage();
startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user); startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
sourceContainer);
} else { } else {
// Could be launching some bookkeeping activity // Could be launching some bookkeeping activity
startActivity(intent, optsBundle); startActivity(intent, optsBundle);

View File

@ -183,6 +183,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onScrollStateChanged(int state) { public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state); super.onScrollStateChanged(state);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onScrollStateChanged: " + state);
}
if (state == SCROLL_STATE_IDLE) { if (state == SCROLL_STATE_IDLE) {
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext()); AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
} }
@ -192,6 +196,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info); super.onInitializeAccessibilityNodeInfo(info);
if (isLayoutSuppressed()) info.setScrollable(false); if (isLayoutSuppressed()) info.setScrollable(false);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
"onInitializeAccessibilityNodeInfo, scrollable: " + info.isScrollable());
}
} }
@Override @Override
@ -199,8 +207,12 @@ public abstract class BaseRecyclerView extends RecyclerView {
final boolean changing = frozen != isLayoutSuppressed(); final boolean changing = frozen != isLayoutSuppressed();
super.setLayoutFrozen(frozen); super.setLayoutFrozen(frozen);
if (changing) { if (changing) {
ActivityContext.lookupContext(getContext()).getDragLayer() if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED); Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen
+ " @ " + Log.getStackTraceString(new Throwable()));
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
}
} }
} }
} }

View File

@ -331,10 +331,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
// ignore events if they happen in padding area // ignore events if they happen in padding area
if (event.getAction() == MotionEvent.ACTION_DOWN if (event.getAction() == MotionEvent.ACTION_DOWN
&& (event.getY() < getPaddingTop() && shouldIgnoreTouchDown(event.getX(), event.getY())) {
|| event.getX() < getPaddingLeft()
|| event.getY() > getHeight() - getPaddingBottom()
|| event.getX() > getWidth() - getPaddingRight())) {
return false; return false;
} }
if (isLongClickable()) { if (isLongClickable()) {
@ -347,6 +344,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
} }
} }
/**
* Returns true if the touch down at the provided position be ignored
*/
protected boolean shouldIgnoreTouchDown(float x, float y) {
return y < getPaddingTop()
|| x < getPaddingLeft()
|| y > getHeight() - getPaddingBottom()
|| x > getWidth() - getPaddingRight();
}
void setStayPressed(boolean stayPressed) { void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed; mStayPressed = stayPressed;
refreshDrawableState(); refreshDrawableState();

View File

@ -814,7 +814,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
if (grantResults.length > 0 if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) { && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startActivitySafely(v, intent, null, null); startActivitySafely(v, intent, null);
} else { } else {
// TODO: Show a snack bar with link to settings // TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission, Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@ -923,6 +923,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
DiscoveryBounce.showForHomeIfNeeded(this); DiscoveryBounce.showForHomeIfNeeded(this);
} }
protected void handlePendingActivityRequest() { }
private void logStopAndResume(int command) { private void logStopAndResume(int command) {
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage(); int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
@ -1423,7 +1424,8 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
if (!isInState(NORMAL)) { if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any // Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume // animations running as part of resume
mStateManager.goToState(NORMAL); mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
this::handlePendingActivityRequest);
} }
// Reset the apps view // Reset the apps view
@ -1860,13 +1862,12 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
} }
@Override @Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item, public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
@Nullable String sourceContainer) {
if (!hasBeenResumed()) { if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the // Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is // recents animation into launcher. Defer launching the activity until Launcher is
// next resumed. // next resumed.
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer)); addOnResumeCallback(() -> startActivitySafely(v, intent, item));
if (mOnDeferredActivityLaunchCallback != null) { if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run(); mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null; mOnDeferredActivityLaunchCallback = null;
@ -1874,7 +1875,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
return true; return true;
} }
boolean success = super.startActivitySafely(v, intent, item, sourceContainer); boolean success = super.startActivitySafely(v, intent, item);
if (success && v instanceof BubbleTextView) { if (success && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away // This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished // from launcher. Since there is no callback for when the activity has finished

View File

@ -58,7 +58,7 @@ import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache; import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PackageUserKey;
@ -410,7 +410,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
enqueueModelUpdateTask(new BaseModelUpdateTask() { enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override @Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>(); final IntSet removedIds = new IntSet();
synchronized (dataModel) { synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) { for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo if (info instanceof WorkspaceItemInfo
@ -418,13 +418,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
&& user.equals(info.user) && user.equals(info.user)
&& info.getIntent() != null && info.getIntent() != null
&& TextUtils.equals(packageName, info.getIntent().getPackage())) { && TextUtils.equals(packageName, info.getIntent().getPackage())) {
removedIds.put(info.id, true /* remove */); removedIds.add(info.id);
} }
} }
} }
if (!removedIds.isEmpty()) { if (!removedIds.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false)); deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
} }
} }
}); });

View File

@ -250,7 +250,8 @@ public abstract class LauncherState implements BaseState<LauncherState> {
} }
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) { if ((this != NORMAL && this != HINT_STATE)
|| !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
return DEFAULT_ALPHA_PROVIDER; return DEFAULT_ALPHA_PROVIDER;
} }
final int centerPage = launcher.getWorkspace().getNextPage(); final int centerPage = launcher.getWorkspace().getNextPage();

View File

@ -39,7 +39,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@ -218,13 +218,16 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
public void onDrop(DragObject d, DragOptions options) { public void onDrop(DragObject d, DragOptions options) {
// Defer onComplete // Defer onComplete
d.dragSource = new DeferredOnComplete(d.dragSource, getContext()); d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
super.onDrop(d, options); super.onDrop(d, options);
StatsLogger logger = mStatsLogManager.logger().withInstanceId(d.logInstanceId);
if (d.originalDragInfo != null) {
logger.withItemInfo(d.originalDragInfo);
}
if (mCurrentAccessibilityAction == UNINSTALL) { if (mCurrentAccessibilityAction == UNINSTALL) {
mStatsLogManager.logger().withInstanceId(d.logInstanceId) logger.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
} else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) { } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
mStatsLogManager.logger().withInstanceId(d.logInstanceId) logger.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
} }
} }
@ -283,8 +286,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
return null; return null;
} }
if (mCurrentAccessibilityAction == DISMISS_PREDICTION) { if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(), // We sent the log event, nothing else left to do
info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
return null; return null;
} }
// else: mCurrentAccessibilityAction == UNINSTALL // else: mCurrentAccessibilityAction == UNINSTALL

View File

@ -309,7 +309,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
// In portrait, we want the pages spaced such that there is no // In portrait, we want the pages spaced such that there is no
// overhang of the previous / next page into the current page viewport. // overhang of the previous / next page into the current page viewport.
// We assume symmetrical padding in portrait mode. // We assume symmetrical padding in portrait mode.
setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1)); int maxInsets = Math.max(insets.left, insets.right);
int maxPadding = Math.max(grid.edgeMarginPx, padding.left + 1);
setPageSpacing(Math.max(maxInsets, maxPadding));
} }

View File

@ -83,7 +83,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
protected final BaseDraggingActivity mLauncher; protected final BaseDraggingActivity mLauncher;
protected final AdapterHolder[] mAH; protected final AdapterHolder[] mAH;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle()); private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher); private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
private final AllAppsStore mAllAppsStore = new AllAppsStore(); private final AllAppsStore mAllAppsStore = new AllAppsStore();
private final Paint mNavBarScrimPaint; private final Paint mNavBarScrimPaint;

View File

@ -41,7 +41,6 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem; import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
@ -270,7 +269,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market, View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false); parent, false);
searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely( searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH)); v, mMarketSearchIntent, null));
return new ViewHolder(searchMarketView); return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate( return new ViewHolder(mLayoutInflater.inflate(

View File

@ -28,7 +28,6 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText; import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
@ -112,8 +111,8 @@ public class AllAppsSearchBarController
return false; return false;
} }
return mLauncher.startActivitySafely(v, return mLauncher.startActivitySafely(v,
PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null, PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null
AppLaunchTracker.CONTAINER_SEARCH); );
} }
@Override @Override

View File

@ -75,6 +75,9 @@ public class AccessibilityManagerCompat {
} }
public static void sendScrollFinishedEventToTest(Context context) { public static void sendScrollFinishedEventToTest(Context context) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendScrollFinishedEventToTest");
}
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return; if (accessibilityManager == null) return;
@ -94,6 +97,9 @@ public class AccessibilityManagerCompat {
AccessibilityEvent.TYPE_ANNOUNCEMENT); AccessibilityEvent.TYPE_ANNOUNCEMENT);
e.setClassName(eventTag); e.setClassName(eventTag);
e.setParcelableData(data); e.setParcelableData(data);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendEventToTest " + e);
}
accessibilityManager.sendAccessibilityEvent(e); accessibilityManager.sendAccessibilityEvent(e);
} }

View File

@ -241,9 +241,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mFolderName.setSelectAllOnFocus(true); mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType() mFolderName.setInputType(mFolderName.getInputType()
& ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
& ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_FLAG_CAP_WORDS); | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
mFolderName.forceDisableSuggestions(!FeatureFlags.FOLDER_NAME_SUGGEST.get()); mFolderName.forceDisableSuggestions(true);
mFooter = findViewById(R.id.folder_footer); mFooter = findViewById(R.id.folder_footer);
@ -741,7 +741,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
@Override @Override
protected View getAccessibilityInitialFocusView() { protected View getAccessibilityInitialFocusView() {
return mContent.getFirstItem(); View firstItem = mContent.getFirstItem();
return firstItem != null ? firstItem : super.getAccessibilityInitialFocusView();
} }
private void closeComplete(boolean wasAnimated) { private void closeComplete(boolean wasAnimated) {

View File

@ -126,7 +126,7 @@ public class LauncherPreviewRenderer {
*/ */
public static class PreviewContext extends ContextWrapper { public static class PreviewContext extends ContextWrapper {
private static final Set<MainThreadInitializedObject> WHITELIST = new HashSet<>( private final Set<MainThreadInitializedObject> mAllowedObjects = new HashSet<>(
Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE, Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE, LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE)); CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE));
@ -160,7 +160,7 @@ public class LauncherPreviewRenderer {
*/ */
public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject, public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
MainThreadInitializedObject.ObjectProvider<T> provider) { MainThreadInitializedObject.ObjectProvider<T> provider) {
if (!WHITELIST.contains(mainThreadInitializedObject)) { if (!mAllowedObjects.contains(mainThreadInitializedObject)) {
throw new IllegalStateException("Leaking unknown objects"); throw new IllegalStateException("Leaking unknown objects");
} }
if (mainThreadInitializedObject == LauncherAppState.INSTANCE) { if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {

View File

@ -15,7 +15,6 @@
*/ */
package com.android.launcher3.logging; package com.android.launcher3.logging;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@ -23,8 +22,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import android.content.Context; import android.content.Context;
import androidx.annotation.Nullable;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.FromState;
@ -403,19 +400,6 @@ public class StatsLogManager implements ResourceBasedOverride {
return mgr; return mgr;
} }
/**
* Log an event with ranked-choice information along with package. Does nothing if event.getId()
* <= 0.
*
* @param rankingEvent an enum implementing EventEnum interface.
* @param instanceId An identifier obtained from an InstanceIdSequence.
* @param packageName the package name of the relevant app, if known (null otherwise).
* @param position the position picked.
*/
public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
int position) {
}
/** /**
* Logs snapshot, or impression of the current workspace. * Logs snapshot, or impression of the current workspace.
*/ */

View File

@ -17,13 +17,7 @@ package com.android.launcher3.model;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.ComponentName;
import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.util.ResourceBasedOverride;
@ -32,28 +26,8 @@ import com.android.launcher3.util.ResourceBasedOverride;
*/ */
public class AppLaunchTracker implements ResourceBasedOverride { public class AppLaunchTracker implements ResourceBasedOverride {
/**
* Derived from LauncherEvent proto.
* TODO: Use proper descriptive constants
*/
public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
public static final String CONTAINER_OVERVIEW = Integer.toString(ContainerType.OVERVIEW);
public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE = public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class); forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
@Nullable String container) { }
public void onStartApp(ComponentName componentName, UserHandle user,
@Nullable String container) { }
public void onDismissApp(ComponentName componentName, UserHandle user,
@Nullable String container){}
public void onReturnedToHome() { } public void onReturnedToHome() { }
} }

View File

@ -56,6 +56,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -348,6 +349,19 @@ public class BgDataModel {
} }
} }
/**
* Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
* items and dynamic/predicted items for the provided {@code userHandle}.
* Note the call is not synchronized over the model, that should be handled by the called.
*/
public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op) {
for (ItemInfo info : itemsIdMap) {
if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
op.accept((WorkspaceItemInfo) info);
}
}
}
public interface Callbacks { public interface Callbacks {
// If the launcher has permission to access deep shortcuts. // If the launcher has permission to access deep shortcuts.
int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0; int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;

View File

@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,23 +47,18 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask {
@Override @Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache(); IconCache iconCache = app.getIconCache();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>(); ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
synchronized (dataModel) { synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) { dataModel.forAllWorkspaceItemInfos(mUser, si -> {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) { ComponentName cn = si.getTargetComponent();
WorkspaceItemInfo si = (WorkspaceItemInfo) info; if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
ComponentName cn = si.getTargetComponent(); && isValidShortcut(si) && cn != null
if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && mPackages.contains(cn.getPackageName())) {
&& isValidShortcut(si) && cn != null iconCache.getTitleAndIcon(si, si.usingLowResIcon());
&& mPackages.contains(cn.getPackageName())) { updatedShortcuts.add(si);
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
updatedShortcuts.add(si);
}
} }
} });
apps.updateIconsAndLabels(mPackages, mUser); apps.updateIconsAndLabels(mPackages, mUser);
} }
bindUpdatedWorkspaceItems(updatedShortcuts); bindUpdatedWorkspaceItems(updatedShortcuts);

View File

@ -20,8 +20,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo; import com.android.launcher3.model.data.PromiseAppInfo;
@ -70,21 +68,18 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
synchronized (dataModel) { synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>(); final HashSet<ItemInfo> updates = new HashSet<>();
for (ItemInfo info : dataModel.itemsIdMap) { dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
if (info instanceof WorkspaceItemInfo) { ComponentName cn = si.getTargetComponent();
WorkspaceItemInfo si = (WorkspaceItemInfo) info; if (si.hasPromiseIconUi() && (cn != null)
ComponentName cn = si.getTargetComponent(); && mInstallInfo.packageName.equals(cn.getPackageName())) {
if (si.hasPromiseIconUi() && (cn != null) si.setInstallProgress(mInstallInfo.progress);
&& mInstallInfo.packageName.equals(cn.getPackageName())) { if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
si.setInstallProgress(mInstallInfo.progress); // Mark this info as broken.
if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) { si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
// Mark this info as broken.
si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
updates.add(si);
} }
updates.add(si);
} }
} });
for (LauncherAppWidgetInfo widget : dataModel.appWidgets) { for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) { if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
@ -94,12 +89,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
} }
if (!updates.isEmpty()) { if (!updates.isEmpty()) {
scheduleCallbackTask(new CallbackTask() { scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
@Override
public void execute(Callbacks callbacks) {
callbacks.bindRestoreItemsChange(updates);
}
});
} }
} }
} }

View File

@ -45,7 +45,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache; import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PackageUserKey;
@ -92,9 +92,11 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
final String[] packages = mPackages; final String[] packages = mPackages;
final int N = packages.length; final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP; final FlagOp flagOp;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages)); final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser); final ItemInfoMatcher matcher = mOp == OP_USER_AVAILABILITY_CHANGE
? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
: ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>(); final HashSet<ComponentName> removedComponents = new HashSet<>();
switch (mOp) { switch (mOp) {
@ -158,19 +160,22 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
flagOp = ums.isUserQuiet(mUser) flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER) ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER); : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp); appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic // We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled()); appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break; break;
} }
default:
flagOp = FlagOp.NO_OP;
break;
} }
bindApplicationsIfNeeded(); bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>(); final IntSet removedShortcuts = new IntSet();
// Shortcuts to keep even if the corresponding app was removed
final IntSet forceKeepShortcuts = new IntSet();
// Update shortcut infos // Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) { if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
@ -180,118 +185,118 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
// For system apps, package manager send OP_UPDATE when an app is enabled. // For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE; final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) { synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) { dataModel.forAllWorkspaceItemInfos(mUser, si -> {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
boolean infoUpdated = false;
boolean shortcutUpdated = false;
// Update shortcuts which use iconResource. boolean infoUpdated = false;
if ((si.iconResource != null) boolean shortcutUpdated = false;
&& packageSet.contains(si.iconResource.packageName)) {
LauncherIcons li = LauncherIcons.obtain(context); // Update shortcuts which use iconResource.
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource); if ((si.iconResource != null)
li.recycle(); && packageSet.contains(si.iconResource.packageName)) {
if (iconInfo != null) { LauncherIcons li = LauncherIcons.obtain(context);
si.bitmap = iconInfo; BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
infoUpdated = true; li.recycle();
if (iconInfo != null) {
si.bitmap = iconInfo;
infoUpdated = true;
}
}
ComponentName cn = si.getTargetComponent();
if (cn != null && matcher.matches(si, cn)) {
String packageName = cn.getPackageName();
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
forceKeepShortcuts.add(si.id);
if (mOp == OP_REMOVE) {
return;
} }
} }
ComponentName cn = si.getTargetComponent(); if (si.isPromise() && isNewApkAvailable) {
if (cn != null && matcher.matches(si, cn)) { boolean isTargetValid = true;
String packageName = cn.getPackageName(); if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut =
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) { new ShortcutRequest(context, mUser)
removedShortcuts.put(si.id, false); .forPackage(cn.getPackageName(),
if (mOp == OP_REMOVE) { si.getDeepShortcutId())
continue; .query(ShortcutRequest.PINNED);
} if (shortcut.isEmpty()) {
} isTargetValid = false;
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
si.getDeepShortcutId())
.query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
} else {
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.put(si.id, true);
continue;
}
} else if (!isTargetValid) {
removedShortcuts.put(si.id, true);
FileLog.e(TAG, "Restored shortcut no longer valid "
+ si.getIntent());
continue;
} else { } else {
si.status = WorkspaceItemInfo.DEFAULT; si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true; infoUpdated = true;
} }
} else if (isNewApkAvailable && removedComponents.contains(cn)) { } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) { if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true; infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.add(si.id);
return;
} }
} } else if (!isTargetValid) {
removedShortcuts.add(si.id);
if (isNewApkAvailable && FileLog.e(TAG, "Restored shortcut no longer valid "
si.itemType == Favorites.ITEM_TYPE_APPLICATION) { + si.getIntent());
iconCache.getTitleAndIcon(si, si.usingLowResIcon()); return;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
infoUpdated = true; infoUpdated = true;
} }
} else if (isNewApkAvailable && removedComponents.contains(cn)) {
int oldRuntimeFlags = si.runtimeStatusFlags; if (updateWorkspaceItemIntent(context, si, packageName)) {
si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags); infoUpdated = true;
if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
} }
} }
if (infoUpdated || shortcutUpdated) { if (isNewApkAvailable
updatedWorkspaceItems.add(si); && si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
} }
if (infoUpdated) {
getModelWriter().updateItemInDatabase(si);
}
} else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup' int oldRuntimeFlags = si.runtimeStatusFlags;
// if the widget has a config activity. In case there is no config si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
// activity, it will be marked as 'restored' during bind. if (si.runtimeStatusFlags != oldRuntimeFlags) {
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; shortcutUpdated = true;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
} }
} }
if (infoUpdated || shortcutUpdated) {
updatedWorkspaceItems.add(si);
}
if (infoUpdated && si.id != ItemInfo.NO_ID) {
getModelWriter().updateItemInDatabase(si);
}
});
for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
& ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup'
// if the widget has a config activity. In case there is no config
// activity, it will be marked as 'restored' during bind.
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
}
} }
} }
bindUpdatedWorkspaceItems(updatedWorkspaceItems); bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) { if (!removedShortcuts.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false)); deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
} }
if (!widgets.isEmpty()) { if (!widgets.isEmpty()) {
@ -319,7 +324,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) { if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser) ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser)) .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
.and(ItemInfoMatcher.ofItemIds(removedShortcuts, true)); .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
deleteAndBindComponentsRemoved(removeMatch); deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue // Remove any queued items from the install queue

View File

@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest;
@ -58,14 +57,14 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>(); MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
HashSet<String> allIds = new HashSet<>(); HashSet<String> allIds = new HashSet<>();
for (ItemInfo itemInfo : dataModel.itemsIdMap) { synchronized (dataModel) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { dataModel.forAllWorkspaceItemInfos(mUser, si -> {
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo; if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) { && mPackageName.equals(si.getIntent().getPackage())) {
keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si); keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
allIds.add(si.getDeepShortcutId()); allIds.add(si.getDeepShortcutId());
} }
} });
} }
final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>(); final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();

View File

@ -23,7 +23,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.shortcuts.ShortcutRequest;
@ -73,27 +72,27 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>(); ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
HashSet<ShortcutKey> removedKeys = new HashSet<>(); HashSet<ShortcutKey> removedKeys = new HashSet<>();
for (ItemInfo itemInfo : dataModel.itemsIdMap) { synchronized (dataModel) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT dataModel.forAllWorkspaceItemInfos(mUser, si -> {
&& mUser.equals(itemInfo.user)) { if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo; if (mIsUserUnlocked) {
if (mIsUserUnlocked) { ShortcutKey key = ShortcutKey.fromItemInfo(si);
ShortcutKey key = ShortcutKey.fromItemInfo(si); ShortcutInfo shortcut = pinnedShortcuts.get(key);
ShortcutInfo shortcut = pinnedShortcuts.get(key); // We couldn't verify the shortcut during loader. If its no longer available
// We couldn't verify the shortcut during loader. If its no longer available // (probably due to clear data), delete the workspace item as well
// (probably due to clear data), delete the workspace item as well if (shortcut == null) {
if (shortcut == null) { removedKeys.add(key);
removedKeys.add(key); return;
continue; }
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
} }
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER; updatedWorkspaceItemInfos.add(si);
si.updateFromDeepShortcutInfo(shortcut, context);
app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
} }
updatedWorkspaceItemInfos.add(si); });
}
} }
bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos); bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
if (!removedKeys.isEmpty()) { if (!removedKeys.isEmpty()) {

View File

@ -53,6 +53,7 @@ import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.ContentWriter;
import java.util.Optional; import java.util.Optional;
@ -285,6 +286,13 @@ public class ItemInfo {
.orElse(LauncherAtom.Application.newBuilder())); .orElse(LauncherAtom.Application.newBuilder()));
break; break;
case ITEM_TYPE_DEEP_SHORTCUT: case ITEM_TYPE_DEEP_SHORTCUT:
itemBuilder
.setShortcut(nullableComponent
.map(component -> LauncherAtom.Shortcut.newBuilder()
.setShortcutName(component.flattenToShortString())
.setShortcutId(ShortcutKey.fromItemInfo(this).getId()))
.orElse(LauncherAtom.Shortcut.newBuilder()));
break;
case ITEM_TYPE_SHORTCUT: case ITEM_TYPE_SHORTCUT:
itemBuilder itemBuilder
.setShortcut(nullableComponent .setShortcut(nullableComponent

View File

@ -174,7 +174,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
public void onClick(View view) { public void onClick(View view) {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent( Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
mItemInfo.getTargetComponent().getPackageName()); mItemInfo.getTargetComponent().getPackageName());
mTarget.startActivitySafely(view, intent, mItemInfo, null); mTarget.startActivitySafely(view, intent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget); AbstractFloatingView.closeAllOpenViews(mTarget);
} }
} }

View File

@ -15,8 +15,6 @@
*/ */
package com.android.launcher3.secondarydisplay; package com.android.launcher3.secondarydisplay;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions; import android.app.ActivityOptions;
@ -327,7 +325,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
if (intent == null) { if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent"); throw new IllegalArgumentException("Input must have a valid intent");
} }
startActivitySafely(v, intent, item, CONTAINER_ALL_APPS); startActivitySafely(v, intent, item);
} }
} }
} }

View File

@ -23,7 +23,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Toast; import android.widget.Toast;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
@ -106,12 +105,12 @@ public class DeepShortcutTextView extends BubbleTextView {
} }
@Override @Override
public boolean onTouchEvent(MotionEvent ev) { protected boolean shouldIgnoreTouchDown(float x, float y) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) { // Show toast if user touches the drag handle (long clicks still start the drag).
// Show toast if user touches the drag handle (long clicks still start the drag). mShowInstructionToast = mDragHandleBounds.contains((int) x, (int) y);
mShowInstructionToast = mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
} // assume the whole view as clickable
return super.onTouchEvent(ev); return false;
} }
@Override @Override

View File

@ -33,6 +33,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Function; import java.util.function.Function;
@ -92,6 +93,11 @@ public class TestInformationHandler implements ResourceBasedOverride {
l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY()); l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
} }
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
}
case TestProtocol.REQUEST_WINDOW_INSETS: { case TestProtocol.REQUEST_WINDOW_INSETS: {
return getUIProperty(Bundle::putParcelable, a -> { return getUIProperty(Bundle::putParcelable, a -> {
WindowInsets insets = a.getWindow() WindowInsets insets = a.getWindow()

View File

@ -81,6 +81,7 @@ public final class TestProtocol {
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list"; public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags"; public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y"; public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
public static final String REQUEST_WINDOW_INSETS = "window-insets"; public static final String REQUEST_WINDOW_INSETS = "window-insets";
public static final String REQUEST_PID = "pid"; public static final String REQUEST_PID = "pid";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss"; public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
@ -106,4 +107,5 @@ public final class TestProtocol {
public static final String PAUSE_NOT_DETECTED = "b/139891609"; public static final String PAUSE_NOT_DETECTED = "b/139891609";
public static final String OVERIEW_NOT_ALLAPPS = "b/156095088"; public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601"; public static final String NO_SWIPE_TO_HOME = "b/158017601";
public static final String NO_SCROLL_END_WIDGETS = "b/160238801";
} }

View File

@ -18,7 +18,6 @@ package com.android.launcher3.touch;
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET; import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET; import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@ -37,8 +36,6 @@ import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo;
@ -72,13 +69,9 @@ public class ItemClickHandler {
/** /**
* Instance used for click handling on items * Instance used for click handling on items
*/ */
public static final OnClickListener INSTANCE = getInstance(null); public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
public static final OnClickListener getInstance(String sourceContainer) { private static void onClick(View v) {
return v -> onClick(v, sourceContainer);
}
private static void onClick(View v, String sourceContainer) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the // Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch). // view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) return; if (v.getWindowToken() == null) return;
@ -88,14 +81,14 @@ public class ItemClickHandler {
Object tag = v.getTag(); Object tag = v.getTag();
if (tag instanceof WorkspaceItemInfo) { if (tag instanceof WorkspaceItemInfo) {
onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer); onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
} else if (tag instanceof FolderInfo) { } else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) { if (v instanceof FolderIcon) {
onClickFolderIcon(v); onClickFolderIcon(v);
} }
} else if (tag instanceof AppInfo) { } else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher, startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher
sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer); );
} else if (tag instanceof LauncherAppWidgetInfo) { } else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) { if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher); onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
@ -191,7 +184,7 @@ public class ItemClickHandler {
// Fallback to using custom market intent. // Fallback to using custom market intent.
Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName); Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
launcher.startActivitySafely(v, intent, item, null); launcher.startActivitySafely(v, intent, item);
} }
/** /**
@ -199,8 +192,7 @@ public class ItemClickHandler {
* *
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}. * @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/ */
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher, public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
@Nullable String sourceContainer) {
if (shortcut.isDisabled()) { if (shortcut.isDisabled()) {
final int disabledFlags = shortcut.runtimeStatusFlags final int disabledFlags = shortcut.runtimeStatusFlags
& WorkspaceItemInfo.FLAG_DISABLED_MASK; & WorkspaceItemInfo.FLAG_DISABLED_MASK;
@ -241,11 +233,10 @@ public class ItemClickHandler {
} }
// Start activities // Start activities
startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer); startAppShortcutOrInfoActivity(v, shortcut, launcher);
} }
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher, private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
@Nullable String sourceContainer) {
TestLogging.recordEvent( TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity"); TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
Intent intent; Intent intent;
@ -274,6 +265,6 @@ public class ItemClickHandler {
// Preload the icon to reduce latency b/w swapping the floating view with the original. // Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */); FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
} }
launcher.startActivitySafely(v, intent, item, sourceContainer); launcher.startActivitySafely(v, intent, item);
} }
} }

View File

@ -17,6 +17,7 @@
package com.android.launcher3.touch; package com.android.launcher3.touch;
import static android.widget.ListPopupWindow.WRAP_CONTENT; import static android.widget.ListPopupWindow.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@ -33,6 +34,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView; import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.OverScroller;
@ -237,7 +239,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
} }
@Override @Override
public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) { public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
LinearLayout taskMenuLayout) {
return LinearLayout.HORIZONTAL; return LinearLayout.HORIZONTAL;
} }
@ -260,4 +263,10 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
} }
return new ChildBounds(childHeight, childWidth, childBottom, childLeft); return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
} }
@SuppressWarnings("SuspiciousNameCombination")
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return rect.left;
}
} }

View File

@ -94,8 +94,9 @@ public interface PagedOrientationHandler {
float getTaskMenuX(float x, View thumbnailView); float getTaskMenuX(float x, View thumbnailView);
float getTaskMenuY(float y, View thumbnailView); float getTaskMenuY(float y, View thumbnailView);
int getTaskMenuWidth(View view); int getTaskMenuWidth(View view);
int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout); int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout);
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp); void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
/** /**
* Maps the velocity from the coordinate plane of the foreground app to that * Maps the velocity from the coordinate plane of the foreground app to that

View File

@ -32,6 +32,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView; import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.OverScroller;
@ -236,8 +237,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
} }
@Override @Override
public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) { public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
return taskMenuLayout.getOrientation(); LinearLayout taskMenuLayout) {
return canRecentsActivityRotate ? taskMenuLayout.getOrientation() : LinearLayout.VERTICAL;
} }
@Override @Override
@ -257,4 +259,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
} }
return new ChildBounds(childWidth, childHeight, childRight, childTop); return new ChildBounds(childWidth, childHeight, childRight, childTop);
} }
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.heightPx - rect.bottom;
}
} }

View File

@ -18,9 +18,11 @@ package com.android.launcher3.touch;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface; import android.view.Surface;
import android.view.View; import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler { public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@ -77,4 +79,9 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
view.setTranslationX(0); view.setTranslationX(0);
view.setTranslationY(translation); view.setTranslationY(translation);
} }
@Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.widthPx - rect.right;
}
} }

View File

@ -81,11 +81,10 @@ public interface ItemInfoMatcher {
} }
/** /**
* Returns a new matcher which returns the opposite boolean value of the provided * Returns a new matcher with returns the opposite value of this.
* {@param matcher}.
*/ */
static ItemInfoMatcher not(ItemInfoMatcher matcher) { default ItemInfoMatcher negate() {
return (info, cn) -> !matcher.matches(info, cn); return (info, cn) -> !matches(info, cn);
} }
static ItemInfoMatcher ofUser(UserHandle user) { static ItemInfoMatcher ofUser(UserHandle user) {
@ -105,7 +104,10 @@ public interface ItemInfoMatcher {
keys.contains(ShortcutKey.fromItemInfo(info)); keys.contains(ShortcutKey.fromItemInfo(info));
} }
static ItemInfoMatcher ofItemIds(IntSparseArrayMap<Boolean> ids, Boolean matchDefault) { /**
return (info, cn) -> ids.get(info.id, matchDefault); * Returns a matcher for items with provided ids
*/
static ItemInfoMatcher ofItemIds(IntSet ids) {
return (info, cn) -> ids.contains(info.id);
} }
} }

View File

@ -46,7 +46,7 @@ public class MainThreadInitializedObject<T> {
if (mValue == null) { if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.myLooper() == Looper.getMainLooper()) {
mValue = TraceHelper.whitelistIpcs("main.thread.object", mValue = TraceHelper.allowIpcs("main.thread.object",
() -> mProvider.get(context.getApplicationContext())); () -> mProvider.get(context.getApplicationContext()));
} else { } else {
try { try {

View File

@ -64,7 +64,7 @@ public class OnboardingPrefs<T extends Launcher> {
private static final Map<String, Integer> MAX_COUNTS; private static final Map<String, Integer> MAX_COUNTS;
static { static {
Map<String, Integer> maxCounts = new ArrayMap<>(3); Map<String, Integer> maxCounts = new ArrayMap<>(4);
maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3); maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5); maxCounts.put(ALL_APPS_COUNT, 5);

View File

@ -78,7 +78,7 @@ public class TraceHelper {
* Temporarily ignore blocking binder calls for the duration of this {@link Supplier}. * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
*/ */
@MainThread @MainThread
public static <T> T whitelistIpcs(String rpcName, Supplier<T> supplier) { public static <T> T allowIpcs(String rpcName, Supplier<T> supplier) {
Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS); Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
try { try {
return supplier.get(); return supplier.get();

View File

@ -292,6 +292,9 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
@Override @Override
public boolean dispatchTouchEvent(MotionEvent ev) { public boolean dispatchTouchEvent(MotionEvent ev) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "BaseDragLayer: " + ev);
}
switch (ev.getAction()) { switch (ev.getAction()) {
case ACTION_DOWN: { case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) { if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
@ -602,13 +605,13 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
*/ */
private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) { private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
if (newWallpaperInfo == null) { if (newWallpaperInfo == null) {
// New wallpaper is static, not live. Thus, blacklist isn't applicable. // Static wallpapers need scrim unless determined otherwise by wallpaperColors.
return true; return true;
} }
ComponentName newWallpaper = newWallpaperInfo.getComponent(); ComponentName newWallpaper = newWallpaperInfo.getComponent();
for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) { for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) { if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
// New wallpaper is blacklisted from showing a scrim. // New wallpaper does not need a scrim.
return false; return false;
} }
} }

View File

@ -36,6 +36,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewOutlineProvider; import android.view.ViewOutlineProvider;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -44,8 +45,6 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce; import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
@ -94,7 +93,6 @@ public class ClipIconView extends View implements ClipPathView {
} }
}; };
private final Launcher mLauncher;
private final int mBlurSizeOutline; private final int mBlurSizeOutline;
private final boolean mIsRtl; private final boolean mIsRtl;
@ -128,7 +126,6 @@ public class ClipIconView extends View implements ClipPathView {
public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) { public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
mBlurSizeOutline = getResources().getDimensionPixelSize( mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline); R.dimen.blur_size_medium_outline);
mIsRtl = Utilities.isRtl(getResources()); mIsRtl = Utilities.isRtl(getResources());
@ -143,10 +140,41 @@ public class ClipIconView extends View implements ClipPathView {
.setStiffness(SpringForce.STIFFNESS_LOW)); .setStiffness(SpringForce.STIFFNESS_LOW));
} }
void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius, /**
boolean isOpening, float scale, float minSize, LayoutParams parentLp, * Update the icon UI to match the provided parameters during an animation frame
boolean isVerticalBarLayout) { */
DeviceProfile dp = mLauncher.getDeviceProfile(); public void update(RectF rect, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening, View container,
DeviceProfile dp, boolean isVerticalBarLayout) {
MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
float dX = mIsRtl
? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
: rect.left - lp.getMarginStart();
float dY = rect.top - lp.topMargin;
container.setTranslationX(dX);
container.setTranslationY(dY);
float minSize = Math.min(lp.width, lp.height);
float scaleX = rect.width() / minSize;
float scaleY = rect.height() / minSize;
float scale = Math.max(1f, Math.min(scaleX, scaleY));
update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
minSize, lp, isVerticalBarLayout, dp);
container.setPivotX(0);
container.setPivotY(0);
container.setScaleX(scale);
container.setScaleY(scale);
container.invalidate();
}
private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
boolean isOpening, float scale, float minSize, MarginLayoutParams parentLp,
boolean isVerticalBarLayout, DeviceProfile dp) {
float dX = mIsRtl float dX = mIsRtl
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width) ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
: rect.left - parentLp.getMarginStart(); : rect.left - parentLp.getMarginStart();
@ -228,8 +256,11 @@ public class ClipIconView extends View implements ClipPathView {
} }
} }
void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening, /**
boolean isVerticalBarLayout) { * Sets the icon for this view as part of initial setup
*/
public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp,
boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) {
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable; mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
if (mIsAdaptiveIcon) { if (mIsAdaptiveIcon) {
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon; boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@ -264,15 +295,14 @@ public class ClipIconView extends View implements ClipPathView {
Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale()); Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
} }
float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
if (isVerticalBarLayout) { if (isVerticalBarLayout) {
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio); lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio);
} else { } else {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio); lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio);
} }
int left = mIsRtl int left = mIsRtl
? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width ? dp.widthPx - lp.getMarginStart() - lp.width
: lp.leftMargin; : lp.leftMargin;
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height); layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);

View File

@ -20,6 +20,7 @@ import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable; import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@ -47,7 +48,6 @@ import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
@ -144,32 +144,8 @@ public class FloatingIconView extends FrameLayout implements
public void update(RectF rect, float alpha, float progress, float shapeProgressStart, public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening) { float cornerRadius, boolean isOpening) {
setAlpha(alpha); setAlpha(alpha);
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening,
InsettableFrameLayout.LayoutParams lp = this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
(InsettableFrameLayout.LayoutParams) getLayoutParams();
DeviceProfile dp = mLauncher.getDeviceProfile();
float dX = mIsRtl
? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
: rect.left - lp.getMarginStart();
float dY = rect.top - lp.topMargin;
setTranslationX(dX);
setTranslationY(dY);
float minSize = Math.min(lp.width, lp.height);
float scaleX = rect.width() / minSize;
float scaleY = rect.height() / minSize;
float scale = Math.max(1f, Math.min(scaleX, scaleY));
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
minSize, lp, mIsVerticalBarLayout);
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
invalidate();
} }
@Override @Override
@ -336,7 +312,8 @@ public class FloatingIconView extends FrameLayout implements
final InsettableFrameLayout.LayoutParams lp = final InsettableFrameLayout.LayoutParams lp =
(InsettableFrameLayout.LayoutParams) getLayoutParams(); (InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge; mBadge = badge;
mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout); mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout,
mLauncher.getDeviceProfile());
if (drawable instanceof AdaptiveIconDrawable) { if (drawable instanceof AdaptiveIconDrawable) {
final int originalHeight = lp.height; final int originalHeight = lp.height;
final int originalWidth = lp.width; final int originalWidth = lp.width;
@ -381,7 +358,7 @@ public class FloatingIconView extends FrameLayout implements
if (mIconLoadResult.isIconLoaded) { if (mIconLoadResult.isIconLoaded) {
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge, setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset); mIconLoadResult.iconOffset);
hideOriginalView(originalView); setIconAndDotVisible(originalView, false);
} else { } else {
mIconLoadResult.onIconLoaded = () -> { mIconLoadResult.onIconLoaded = () -> {
if (cancellationSignal.isCanceled()) { if (cancellationSignal.isCanceled()) {
@ -392,22 +369,13 @@ public class FloatingIconView extends FrameLayout implements
mIconLoadResult.iconOffset); mIconLoadResult.iconOffset);
setVisibility(VISIBLE); setVisibility(VISIBLE);
hideOriginalView(originalView); setIconAndDotVisible(originalView, false);
}; };
mLoadIconSignal = cancellationSignal; mLoadIconSignal = cancellationSignal;
} }
} }
} }
private void hideOriginalView(View originalView) {
if (originalView instanceof IconLabelDotView) {
((IconLabelDotView) originalView).setIconVisible(false);
((IconLabelDotView) originalView).setForceHideDot(true);
} else {
originalView.setVisibility(INVISIBLE);
}
}
@WorkerThread @WorkerThread
@SuppressWarnings("WrongThread") @SuppressWarnings("WrongThread")
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) { private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
@ -477,7 +445,7 @@ public class FloatingIconView extends FrameLayout implements
} }
if (!mIsOpening) { if (!mIsOpening) {
// When closing an app, we want the item on the workspace to be invisible immediately // When closing an app, we want the item on the workspace to be invisible immediately
hideOriginalView(mOriginalIcon); setIconAndDotVisible(mOriginalIcon, false);
} }
} }
@ -573,12 +541,7 @@ public class FloatingIconView extends FrameLayout implements
if (hideOriginal) { if (hideOriginal) {
if (isOpening) { if (isOpening) {
if (originalView instanceof BubbleTextView) { setIconAndDotVisible(originalView, true);
((BubbleTextView) originalView).setIconVisible(true);
((BubbleTextView) originalView).setForceHideDot(false);
} else {
originalView.setVisibility(VISIBLE);
}
view.finish(dragLayer); view.finish(dragLayer);
} else { } else {
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer); view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
@ -615,12 +578,10 @@ public class FloatingIconView extends FrameLayout implements
}); });
if (originalView instanceof IconLabelDotView) { if (originalView instanceof IconLabelDotView) {
IconLabelDotView view = (IconLabelDotView) originalView;
fade.addListener(new AnimatorListenerAdapter() { fade.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
view.setIconVisible(true); setIconAndDotVisible(originalView, true);
view.setForceHideDot(false);
} }
}); });
} }

View File

@ -15,10 +15,24 @@
*/ */
package com.android.launcher3.views; package com.android.launcher3.views;
import android.view.View;
/** /**
* A view that has an icon, label, and notification dot. * A view that has an icon, label, and notification dot.
*/ */
public interface IconLabelDotView { public interface IconLabelDotView {
void setIconVisible(boolean visible); void setIconVisible(boolean visible);
void setForceHideDot(boolean hide); void setForceHideDot(boolean hide);
/**
* Sets the visibility of icon and dot of the view
*/
static void setIconAndDotVisible(View view, boolean visible) {
if (view instanceof IconLabelDotView) {
((IconLabelDotView) view).setIconVisible(visible);
((IconLabelDotView) view).setForceHideDot(!visible);
} else {
view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
} }

View File

@ -220,7 +220,7 @@ public class OptionsPopupView extends ArrowPopup
if (!TextUtils.isEmpty(pickerPackage)) { if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage); intent.setPackage(pickerPackage);
} }
return launcher.startActivitySafely(v, intent, dummyInfo(intent), null); return launcher.startActivitySafely(v, intent, dummyInfo(intent));
} }
static WorkspaceItemInfo dummyInfo(Intent intent) { static WorkspaceItemInfo dummyInfo(Intent intent) {

View File

@ -20,8 +20,10 @@ import android.util.Log;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator; import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
import java.util.ArrayList; import java.util.ArrayList;
@ -32,8 +34,8 @@ import java.util.Iterator;
* methods accordingly. * methods accordingly.
*/ */
public class WidgetsDiffReporter { public class WidgetsDiffReporter {
private static final boolean DEBUG = false; private static final boolean DEBUG = Utilities.IS_RUNNING_IN_TEST_HARNESS; // b/160238801
private static final String TAG = "WidgetsDiffReporter"; private static final String TAG = TestProtocol.NO_SCROLL_END_WIDGETS;
private final IconCache mIconCache; private final IconCache mIconCache;
private final RecyclerView.Adapter mListener; private final RecyclerView.Adapter mListener;

View File

@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -38,8 +39,10 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView; import com.android.launcher3.views.TopRoundedCornerView;
@ -68,6 +71,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
} }
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsFullSheet: " + ev);
}
return super.dispatchTouchEvent(ev);
}
public WidgetsFullSheet(Context context, AttributeSet attrs) { public WidgetsFullSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0); this(context, attrs, 0);
} }

View File

@ -120,6 +120,9 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
public int getCurrentScrollY() { public int getCurrentScrollY() {
// Skip early if widgets are not bound. // Skip early if widgets are not bound.
if (isModelNotReady() || getChildCount() == 0) { if (isModelNotReady() || getChildCount() == 0) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "getCurrentScrollY: -1");
}
return -1; return -1;
} }
@ -128,6 +131,10 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
int y = (child.getMeasuredHeight() * rowIndex); int y = (child.getMeasuredHeight() * rowIndex);
int offset = getLayoutManager().getDecoratedTop(child); int offset = getLayoutManager().getDecoratedTop(child);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
"getCurrentScrollY: " + (getPaddingTop() + y - offset));
}
return getPaddingTop() + y - offset; return getPaddingTop() + y - offset;
} }
@ -158,13 +165,23 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset); mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
} }
if (mTouchDownOnScroller) { if (mTouchDownOnScroller) {
return mScrollbar.handleTouchEvent(e, mFastScrollerOffset); final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result);
}
return result;
}
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false");
} }
return false; return false;
} }
@Override @Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) { public void onTouchEvent(RecyclerView rv, MotionEvent e) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent");
}
if (mTouchDownOnScroller) { if (mTouchDownOnScroller) {
mScrollbar.handleTouchEvent(e, mFastScrollerOffset); mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
} }
@ -172,5 +189,31 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
@Override @Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent "
+ disallowIntercept);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean result = super.dispatchTouchEvent(ev);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: "
+ getScrollState()
+ " can scroll: " + getLayoutManager().canScrollVertically()
+ " result: " + result
+ " layout suppressed: " + isLayoutSuppressed()
+ " event: " + ev);
}
return result;
}
@Override
public void stopNestedScroll() {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll");
}
super.stopNestedScroll();
} }
} }

View File

@ -1,54 +0,0 @@
/*
* 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.systemui.plugins;
import android.content.ComponentName;
import android.os.UserHandle;
import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
* Plugin interface which sends app launch events.
*/
@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION)
public interface AppLaunchEventsPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS";
int VERSION = 1;
/**
* Receives onStartShortcut event from
* {@link com.android.launcher3.appprediction.PredictionAppTracker}.
*/
void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container);
/**
* Receives onStartApp event from
* {@link com.android.launcher3.appprediction.PredictionAppTracker}.
*/
void onStartApp(ComponentName componentName, UserHandle user, String container);
/**
* Receives onDismissApp event from
* {@link com.android.launcher3.appprediction.PredictionAppTracker}.
*/
void onDismissApp(ComponentName componentName, UserHandle user, String container);
/**
* Receives onReturnedToHome event from
* {@link com.android.launcher3.appprediction.PredictionAppTracker}.
*/
void onReturnedToHome();
}

View File

@ -29,6 +29,7 @@
<receiver <receiver
android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig" android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
android:exported="true"
android:label="No Config"> android:label="No Config">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@ -39,6 +40,7 @@
<receiver <receiver
android:name="com.android.launcher3.testcomponent.AppWdigetHidden" android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
android:exported="true"
android:label="Hidden widget"> android:label="Hidden widget">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@ -49,6 +51,7 @@
<receiver <receiver
android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig" android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
android:exported="true"
android:label="With Config"> android:label="With Config">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@ -58,12 +61,14 @@
</receiver> </receiver>
<activity <activity
android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"> android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity"> <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" /> <action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@ -72,6 +77,7 @@
<activity <activity
android:name="com.android.launcher3.testcomponent.RequestPinItemActivity" android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
android:icon="@drawable/test_drawable_pin_item" android:icon="@drawable/test_drawable_pin_item"
android:exported="true"
android:label="Test Pin Item"> android:label="Test Pin Item">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -102,6 +108,7 @@
android:stateNotNeeded="true" android:stateNotNeeded="true"
android:taskAffinity="" android:taskAffinity=""
android:theme="@android:style/Theme.DeviceDefault.Light" android:theme="@android:style/Theme.DeviceDefault.Light"
android:exported="true"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -114,6 +121,7 @@
<activity <activity
android:name="com.android.launcher3.testcomponent.BaseTestingActivity" android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
android:label="LauncherTestApp" android:label="LauncherTestApp"
android:exported="true"
android:taskAffinity="com.android.launcher3.testcomponent.Affinity1"> android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -128,6 +136,7 @@
</activity> </activity>
<activity-alias android:name="Activity2" <activity-alias android:name="Activity2"
android:label="TestActivity2" android:label="TestActivity2"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -136,6 +145,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity3" <activity-alias android:name="Activity3"
android:label="TestActivity3" android:label="TestActivity3"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -144,6 +154,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity4" <activity-alias android:name="Activity4"
android:label="TestActivity4" android:label="TestActivity4"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -152,6 +163,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity5" <activity-alias android:name="Activity5"
android:label="TestActivity5" android:label="TestActivity5"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -160,6 +172,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity6" <activity-alias android:name="Activity6"
android:label="TestActivity6" android:label="TestActivity6"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -168,6 +181,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity7" <activity-alias android:name="Activity7"
android:label="TestActivity7" android:label="TestActivity7"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -176,6 +190,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity8" <activity-alias android:name="Activity8"
android:label="TestActivity8" android:label="TestActivity8"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -184,6 +199,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity9" <activity-alias android:name="Activity9"
android:label="TestActivity9" android:label="TestActivity9"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -192,6 +208,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity10" <activity-alias android:name="Activity10"
android:label="TestActivity10" android:label="TestActivity10"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
@ -200,6 +217,7 @@
</activity-alias> </activity-alias>
<activity-alias android:name="Activity11" <activity-alias android:name="Activity11"
android:label="TestActivity11" android:label="TestActivity11"
android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity"> android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@ -26,6 +26,7 @@
<activity <activity
android:name="Activity1" android:name="Activity1"
android:icon="@mipmap/ic_launcher1" android:icon="@mipmap/ic_launcher1"
android:exported="true"
android:label="Aardwolf"> android:label="Aardwolf">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@ -73,20 +73,12 @@ public class ActivityLeakTracker implements Application.ActivityLifecycleCallbac
} }
public boolean noLeakedActivities() { public boolean noLeakedActivities() {
int liveActivities = 0;
int destroyedActivities = 0;
for (Activity activity : mActivities.keySet()) { for (Activity activity : mActivities.keySet()) {
if (activity.isDestroyed()) { if (activity.isDestroyed()) {
++destroyedActivities; return false;
} else {
++liveActivities;
} }
} }
if (liveActivities > 2) return false; return mActivities.size() <= 2;
// It's OK to have 1 leaked activity if no active activities exist.
return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
} }
} }

View File

@ -152,6 +152,7 @@ public final class LauncherInstrumentation {
private static final String WIDGETS_RES_ID = "widgets_list_view"; private static final String WIDGETS_RES_ID = "widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container"; private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
public static final int WAIT_TIME_MS = 10000; public static final int WAIT_TIME_MS = 10000;
public static final int LONG_WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null); private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@ -635,9 +636,11 @@ public final class LauncherInstrumentation {
Parcelable executeAndWaitForEvent(Runnable command, Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) { UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
try { try {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: before");
final AccessibilityEvent event = final AccessibilityEvent event =
mInstrumentation.getUiAutomation().executeAndWaitForEvent( mInstrumentation.getUiAutomation().executeAndWaitForEvent(
command, eventFilter, WAIT_TIME_MS); command, eventFilter, LONG_WAIT_TIME_MS);
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: after");
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event); assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
final Parcelable parcelableData = event.getParcelableData(); final Parcelable parcelableData = event.getParcelableData();
event.recycle(); event.recycle();
@ -1094,7 +1097,10 @@ public final class LauncherInstrumentation {
executeAndWaitForEvent( executeAndWaitForEvent(
() -> linearGesture( () -> linearGesture(
startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE), startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()), event -> {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "scroll: received event: " + event);
return TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName());
},
() -> "Didn't receive a scroll end message: " + startX + ", " + startY () -> "Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY); + ", " + endX + ", " + endY);
} }

Some files were not shown because too many files have changed in this diff Show More