Merging from ub-launcher3-rvc-dev @ build 6617269
Bug: 150504032 Test: manual, presubmit on the source branch https: //android-launcher.teams.x20web.corp.google.com/merge/ub-launcher3-rvc-dev_master_6617269.html Change-Id: I2a168bfa8e3979ade26ab1d3cddbbae84cf79590
This commit is contained in:
commit
0b51b916b0
|
@ -67,7 +67,10 @@ LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
|
|||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, src) \
|
||||
$(call all-java-files-under, src_shortcuts_overrides) \
|
||||
$(call all-java-files-under, src_ui_overrides)
|
||||
$(call all-java-files-under, src_ui_overrides) \
|
||||
$(call all-java-files-under, ext_tests/src)
|
||||
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
|
||||
|
||||
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
|
||||
# Proguard is disable for testing. Derivarive prjects to keep proguard enabled
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="test_information_handler_class" translatable="false">com.android.launcher3.testing.DebugTestInformationHandler</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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.launcher3.testing;
|
||||
|
||||
import static android.graphics.Bitmap.Config.ARGB_8888;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.system.Os;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Class to handle requests from tests, including debug ones.
|
||||
*/
|
||||
public class DebugTestInformationHandler extends TestInformationHandler {
|
||||
private static LinkedList sLeaks;
|
||||
private static Collection<String> sEvents;
|
||||
|
||||
public DebugTestInformationHandler(Context context) {
|
||||
init(context);
|
||||
}
|
||||
|
||||
private static void runGcAndFinalizersSync() {
|
||||
Runtime.getRuntime().gc();
|
||||
Runtime.getRuntime().runFinalization();
|
||||
|
||||
final CountDownLatch fence = new CountDownLatch(1);
|
||||
createFinalizationObserver(fence);
|
||||
try {
|
||||
do {
|
||||
Runtime.getRuntime().gc();
|
||||
Runtime.getRuntime().runFinalization();
|
||||
} while (!fence.await(100, TimeUnit.MILLISECONDS));
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the observer in the scope of a method to minimize the chance that
|
||||
// it remains live in a DEX/machine register at the point of the fence guard.
|
||||
// This must be kept to avoid R8 inlining it.
|
||||
@Keep
|
||||
private static void createFinalizationObserver(CountDownLatch fence) {
|
||||
new Object() {
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
fence.countDown();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle call(String method) {
|
||||
final Bundle response = new Bundle();
|
||||
switch (method) {
|
||||
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
|
||||
return getLauncherUIProperty(Bundle::putInt,
|
||||
l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
|
||||
TestProtocol.sDebugTracing = true;
|
||||
return response;
|
||||
|
||||
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
|
||||
TestProtocol.sDebugTracing = false;
|
||||
return response;
|
||||
|
||||
case TestProtocol.REQUEST_PID: {
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
|
||||
runGcAndFinalizersSync();
|
||||
Debug.MemoryInfo mem = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(mem);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_JAVA_LEAK: {
|
||||
if (sLeaks == null) sLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty the memory.
|
||||
final int leakSize = 1024 * 1024;
|
||||
final byte[] bytes = new byte[leakSize];
|
||||
for (int i = 0; i < leakSize; i += 239) {
|
||||
bytes[i] = (byte) (i % 256);
|
||||
}
|
||||
sLeaks.add(bytes);
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_NATIVE_LEAK: {
|
||||
if (sLeaks == null) sLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty a bitmap.
|
||||
final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888);
|
||||
bitmap.eraseColor(Color.RED);
|
||||
sLeaks.add(bitmap);
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_VIEW_LEAK: {
|
||||
if (sLeaks == null) sLeaks = new LinkedList();
|
||||
sLeaks.add(new View(mContext));
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_START_EVENT_LOGGING: {
|
||||
sEvents = new ArrayList<>();
|
||||
TestLogging.setEventConsumer(
|
||||
(sequence, event) -> {
|
||||
final Collection<String> events = sEvents;
|
||||
if (events != null) {
|
||||
synchronized (events) {
|
||||
events.add(sequence + '/' + event);
|
||||
}
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
|
||||
TestLogging.setEventConsumer(null);
|
||||
sEvents = null;
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
|
||||
synchronized (sEvents) {
|
||||
response.putStringArrayList(
|
||||
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.call(method);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,8 +96,21 @@ enum Attribute {
|
|||
ADD_TO_HOMESCREEN = 6; // play install + launcher home setting
|
||||
ALLAPPS_PREDICTION = 7; // from prediction bar in all apps container
|
||||
HOTSEAT_PREDICTION = 8; // from prediction bar in hotseat container
|
||||
SUGGESTED_LABEL = 9; // folder icon's label was suggested
|
||||
MANUAL_LABEL = 10; // folder icon's label was manually edited
|
||||
|
||||
// Folder's label is one of the non-empty suggested values.
|
||||
SUGGESTED_LABEL = 9;
|
||||
|
||||
// Folder's label is non-empty, manually entered by the user
|
||||
// and different from any of suggested values.
|
||||
MANUAL_LABEL = 10;
|
||||
|
||||
// Folder's label is not yet assigned( i.e., title == null).
|
||||
// Eligible for auto-labeling.
|
||||
UNLABELED = 11;
|
||||
|
||||
// Folder's label is empty(i.e., title == "").
|
||||
// Not eligible for auto-labeling.
|
||||
EMPTY_LABEL = 12;
|
||||
}
|
||||
|
||||
// Main app icons
|
||||
|
|
|
@ -52,8 +52,7 @@
|
|||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
|
||||
android:resizeableActivity="true"
|
||||
android:resumeWhilePausing="true"
|
||||
android:taskAffinity="${packageName}.launcher"
|
||||
android:exported="true"
|
||||
android:taskAffinity=""
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
|
@ -22,10 +22,15 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.launcher3">
|
||||
|
||||
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
|
||||
<permission
|
||||
android:name="${packageName}.permission.HOTSEAT_EDU"
|
||||
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
|
||||
|
||||
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
|
||||
android:fullBackupOnly="true"
|
||||
|
@ -89,17 +94,28 @@
|
|||
android:clearTaskOnLaunch="true"
|
||||
android:exported="false"/>
|
||||
|
||||
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity="${packageName}.launcher"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true">
|
||||
<activity
|
||||
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".hybridhotseat.HotseatEduActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:noHistory="true"
|
||||
android:launchMode="singleTask"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:permission="${packageName}.permission.HOTSEAT_EDU">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
|
|
|
@ -13,25 +13,30 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.quickstep.fallback.RecentsRootView
|
||||
<com.android.launcher3.LauncherRootView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/drag_layer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.android.quickstep.fallback.FallbackRecentsView
|
||||
android:id="@id/overview_panel"
|
||||
<com.android.quickstep.fallback.RecentsDragLayer
|
||||
android:id="@+id/drag_layer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:outlineProvider="none"
|
||||
android:theme="@style/HomeScreenElementTheme" />
|
||||
android:clipChildren="false">
|
||||
|
||||
<include
|
||||
android:id="@+id/overview_actions_view"
|
||||
layout="@layout/overview_actions_container" />
|
||||
<com.android.quickstep.fallback.FallbackRecentsView
|
||||
android:id="@id/overview_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:outlineProvider="none"
|
||||
android:theme="@style/HomeScreenElementTheme" />
|
||||
|
||||
</com.android.quickstep.fallback.RecentsRootView>
|
||||
<include
|
||||
android:id="@+id/overview_actions_view"
|
||||
layout="@layout/overview_actions_container" />
|
||||
|
||||
</com.android.quickstep.fallback.RecentsDragLayer>
|
||||
</com.android.launcher3.LauncherRootView>
|
||||
|
|
|
@ -72,33 +72,42 @@
|
|||
android:layout_height="0dp"
|
||||
launcher:containerType="hotseat" />
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="@dimen/bottom_sheet_edu_padding">
|
||||
|
||||
<Button
|
||||
android:id="@+id/turn_predictions_on"
|
||||
android:layout_width="wrap_content"
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/hotseat_edu_accept"
|
||||
android:textAlignment="textEnd"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight=".4">
|
||||
<Button
|
||||
android:id="@+id/no_thanks"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/hotseat_edu_dismiss"
|
||||
android:textColor="@android:color/white"/>
|
||||
</FrameLayout>
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/no_thanks"
|
||||
android:text="@string/hotseat_edu_dismiss"
|
||||
android:layout_gravity="start"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textColor="@android:color/white" />
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight=".6">
|
||||
<Button
|
||||
android:id="@+id/turn_predictions_on"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="end"
|
||||
android:text="@string/hotseat_edu_accept"
|
||||
android:textColor="@android:color/white"/>
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
package com.android.launcher3.appprediction;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_RANKED;
|
||||
|
||||
import android.app.prediction.AppPredictor;
|
||||
import android.app.prediction.AppTarget;
|
||||
|
@ -26,6 +28,7 @@ import android.content.ComponentName;
|
|||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
|
||||
|
@ -38,8 +41,6 @@ import com.android.launcher3.allapps.AllAppsContainerView;
|
|||
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
|
||||
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
|
||||
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
|
@ -51,6 +52,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
@ -306,40 +308,25 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
|
|||
}
|
||||
|
||||
/**
|
||||
* Logs ranking info for launched app within all apps prediction.
|
||||
* Returns ranking info for the app within all apps prediction.
|
||||
* Only applicable when {@link ItemInfo#itemType} is one of the followings:
|
||||
* {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
|
||||
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
|
||||
* {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
|
||||
*/
|
||||
public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
|
||||
if (itemInfo.getTargetComponent() == null || itemInfo.user == null
|
||||
|| (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
|
||||
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|
||||
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
|
||||
return;
|
||||
}
|
||||
public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) {
|
||||
Optional<ComponentKey> componentKey = Optional.ofNullable(itemInfo)
|
||||
.filter(item -> item.itemType == ITEM_TYPE_APPLICATION
|
||||
|| item.itemType == ITEM_TYPE_SHORTCUT
|
||||
|| item.itemType == ITEM_TYPE_DEEP_SHORTCUT)
|
||||
.map(ItemInfo::getTargetComponent)
|
||||
.map(componentName -> new ComponentKey(componentName, itemInfo.user));
|
||||
|
||||
Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
|
||||
final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
|
||||
final List<ComponentKeyMapper> predictedApps = getCurrentState().apps;
|
||||
OptionalInt rank = IntStream.range(0, predictedApps.size())
|
||||
.filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
|
||||
.findFirst();
|
||||
if (!rank.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
|
||||
atomBuilder.setRank(rank.getAsInt());
|
||||
atomBuilder.setContainerInfo(
|
||||
LauncherAtom.ContainerInfo.newBuilder().setPredictionContainer(
|
||||
LauncherAtom.PredictionContainer.newBuilder().build()).build());
|
||||
launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_RANKED, instanceId,
|
||||
atomBuilder.build());
|
||||
return componentKey.map(key -> IntStream.range(0, getCurrentState().apps.size())
|
||||
.filter(index -> key.equals(getCurrentState().apps.get(index).getComponentKey()))
|
||||
.findFirst()).orElseGet(OptionalInt::empty);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill in predicted_rank field based on app prediction.
|
||||
* Only applicable when {@link ItemInfo#itemType} is one of the followings:
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.launcher3.hybridhotseat;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.ActivityTracker;
|
||||
|
||||
/**
|
||||
* Proxy activity to return user to home screen and show halfsheet education
|
||||
*/
|
||||
public class HotseatEduActivity extends Activity {
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(getPackageName())
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
new HotseatActivityTracker<>().addToIntent(homeIntent);
|
||||
startActivity(homeIntent);
|
||||
finish();
|
||||
}
|
||||
|
||||
static class HotseatActivityTracker<T extends QuickstepLauncher> implements
|
||||
ActivityTracker.SchedulerCallback {
|
||||
|
||||
@Override
|
||||
public boolean init(BaseActivity activity, boolean alreadyOnHome) {
|
||||
QuickstepLauncher launcher = (QuickstepLauncher) activity;
|
||||
if (launcher != null && launcher.getHotseatPredictionController() != null) {
|
||||
launcher.getHotseatPredictionController().showEdu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -84,7 +84,7 @@ public class HotseatEduController {
|
|||
}
|
||||
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
|
||||
() -> mLauncher.startActivity(getSettingsIntent()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,9 +114,9 @@ public class HotseatEduController {
|
|||
if (!putIntoFolder.isEmpty()) {
|
||||
ItemInfo firstItem = putIntoFolder.get(0);
|
||||
FolderInfo folderInfo = new FolderInfo();
|
||||
folderInfo.setTitle("");
|
||||
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
|
||||
firstItem.screenId, firstItem.cellX, firstItem.cellY);
|
||||
folderInfo.setTitle("", mLauncher.getModelWriter());
|
||||
folderInfo.contents.addAll(putIntoFolder);
|
||||
for (int i = 0; i < folderInfo.contents.size(); i++) {
|
||||
ItemInfo item = folderInfo.contents.get(i);
|
||||
|
@ -237,7 +237,7 @@ public class HotseatEduController {
|
|||
< mLauncher.getDeviceProfile().inv.numHotseatIcons) {
|
||||
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
|
||||
() -> mLauncher.startActivity(getSettingsIntent()));
|
||||
} else {
|
||||
new ArrowTipView(mLauncher).show(
|
||||
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
|
||||
|
@ -265,7 +265,7 @@ public class HotseatEduController {
|
|||
requiresMigration ? R.string.hotseat_tip_no_empty_slots
|
||||
: R.string.hotseat_auto_enrolled),
|
||||
mHotseat.getTop());
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
|
||||
finishOnboarding();
|
||||
}
|
||||
}
|
||||
|
@ -281,5 +281,9 @@ public class HotseatEduController {
|
|||
mActiveDialog.setHotseatEduController(this);
|
||||
mActiveDialog.show(mPredictedApps);
|
||||
}
|
||||
|
||||
static Intent getSettingsIntent() {
|
||||
return new Intent(SETTINGS_ACTION).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,13 +111,13 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
|
||||
mHotseatEduController.moveHotseatItems();
|
||||
mHotseatEduController.finishOnboarding();
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
|
||||
}
|
||||
|
||||
private void onDismiss(View v) {
|
||||
mHotseatEduController.showDimissTip();
|
||||
mHotseatEduController.finishOnboarding();
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_DENY);
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
|
||||
handleClose(true);
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
}
|
||||
AbstractFloatingView.closeAllOpenViews(mLauncher);
|
||||
attachToContainer();
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_SEEN);
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
|
||||
animateOpen();
|
||||
populatePreview(predictions);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ package com.android.launcher3.hybridhotseat;
|
|||
|
||||
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -29,9 +30,9 @@ import android.app.prediction.AppPredictor;
|
|||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
@ -54,7 +55,8 @@ import com.android.launcher3.appprediction.DynamicItemCache;
|
|||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
|
@ -69,6 +71,7 @@ import com.android.launcher3.uioverrides.QuickstepLauncher;
|
|||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.launcher3.views.ArrowTipView;
|
||||
import com.android.launcher3.views.Snackbar;
|
||||
|
||||
|
@ -120,9 +123,18 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
private final View.OnLongClickListener mPredictionLongClickListener = v -> {
|
||||
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
|
||||
if (mLauncher.getWorkspace().isSwitchingState()) return false;
|
||||
if (!mLauncher.getOnboardingPrefs().getBoolean(
|
||||
OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
|
||||
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(getSettingsIntent()));
|
||||
mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
|
||||
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
}
|
||||
// Start the drag
|
||||
mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
public HotseatPredictionController(Launcher launcher) {
|
||||
|
@ -154,36 +166,35 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
* Shows appropriate hotseat education based on prediction enabled and migration states.
|
||||
*/
|
||||
public void showEdu() {
|
||||
if (mComponentKeyMappers.isEmpty()) {
|
||||
// launcher has empty predictions set
|
||||
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(
|
||||
new Intent(SETTINGS_ACTION)));
|
||||
} else if (isEduSeen()) {
|
||||
// user has already went through education
|
||||
new ArrowTipView(mLauncher).show(
|
||||
mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
|
||||
mHotseat.getTop());
|
||||
} else {
|
||||
HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
|
||||
this::createPredictor);
|
||||
eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
|
||||
eduController.showEdu();
|
||||
}
|
||||
mLauncher.getStateManager().goToState(NORMAL, true, () -> {
|
||||
if (mComponentKeyMappers.isEmpty()) {
|
||||
// launcher has empty predictions set
|
||||
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(getSettingsIntent()));
|
||||
} else if (isEduSeen() || getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
|
||||
showDiscoveryTip();
|
||||
} else {
|
||||
HotseatEduController eduController = new HotseatEduController(mLauncher,
|
||||
mRestoreHelper,
|
||||
this::createPredictor);
|
||||
eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
|
||||
eduController.showEdu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows educational tip for hotseat if user does not go through Tips app.
|
||||
*/
|
||||
public void showDiscoveryTip() {
|
||||
if (getPredictedIcons().size() == mHotSeatItemsCount) {
|
||||
private void showDiscoveryTip() {
|
||||
if (getPredictedIcons().isEmpty()) {
|
||||
new ArrowTipView(mLauncher).show(
|
||||
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
|
||||
} else {
|
||||
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
|
||||
R.string.hotseat_prediction_settings, null,
|
||||
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
|
||||
() -> mLauncher.startActivity(getSettingsIntent()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,24 +672,25 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
if (!rank.isPresent()) {
|
||||
return;
|
||||
}
|
||||
LauncherAtom.PredictedHotseatContainer.Builder containerBuilder =
|
||||
LauncherAtom.PredictedHotseatContainer.newBuilder();
|
||||
LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
|
||||
|
||||
int cardinality = 0;
|
||||
for (PredictedAppIcon icon : getPredictedIcons()) {
|
||||
ItemInfo info = (ItemInfo) icon.getTag();
|
||||
cardinality |= 1 << info.screenId;
|
||||
}
|
||||
|
||||
PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
|
||||
containerBuilder.setCardinality(cardinality);
|
||||
atomBuilder.setRank(rank.getAsInt());
|
||||
if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
|
||||
containerBuilder.setIndex(rank.getAsInt());
|
||||
}
|
||||
atomBuilder.setContainerInfo(
|
||||
LauncherAtom.ContainerInfo.newBuilder().setPredictedHotseatContainer(
|
||||
containerBuilder).build());
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_RANKED, instanceId,
|
||||
atomBuilder.build());
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withInstanceId(instanceId)
|
||||
.withRank(rank.getAsInt())
|
||||
.withContainerInfo(ContainerInfo.newBuilder()
|
||||
.setPredictedHotseatContainer(containerBuilder)
|
||||
.build())
|
||||
.log(LAUNCHER_HOTSEAT_RANKED);
|
||||
}
|
||||
|
||||
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView implements
|
|||
LauncherAccessibilityDelegate.AccessibilityActionHandler {
|
||||
|
||||
private static final int RING_SHADOW_COLOR = 0x99000000;
|
||||
private static final float RING_EFFECT_RATIO = 0.08f;
|
||||
private static final float RING_EFFECT_RATIO = 0.095f;
|
||||
|
||||
boolean mIsDrawingDot = false;
|
||||
private final DeviceProfile mDeviceProfile;
|
||||
|
|
|
@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherState.NORMAL;
|
|||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
|
||||
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
|
||||
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
|
||||
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
|
||||
|
@ -45,9 +46,9 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
|||
import com.android.launcher3.appprediction.PredictionUiStateManager;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.hybridhotseat.HotseatEduController;
|
||||
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
|
@ -80,6 +81,7 @@ import java.io.FileDescriptor;
|
|||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class QuickstepLauncher extends BaseQuickstepLauncher {
|
||||
|
@ -107,27 +109,17 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
if (HotseatEduController.HOTSEAT_EDU_ACTION.equals(intent.getAction())
|
||||
&& mHotseatPredictionController != null) {
|
||||
boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags()
|
||||
& Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
|
||||
!= Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
|
||||
getStateManager().goToState(NORMAL, alreadyOnHome, () -> {
|
||||
mHotseatPredictionController.showEdu();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
|
||||
super.logAppLaunch(info, instanceId);
|
||||
StatsLogger logger = getStatsLogManager()
|
||||
.logger().withItemInfo(info).withInstanceId(instanceId);
|
||||
OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info);
|
||||
allAppsRank.ifPresent(logger::withRank);
|
||||
logger.log(LAUNCHER_APP_LAUNCH_TAP);
|
||||
|
||||
if (mHotseatPredictionController != null) {
|
||||
mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
|
||||
}
|
||||
PredictionUiStateManager.INSTANCE.get(this).logLaunchedAppRankingInfo(info, instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
|||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
|
||||
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
|
||||
|
@ -90,6 +91,10 @@ public class QuickstepAtomicAnimationFactory extends
|
|||
protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
|
||||
+ MY_ANIM_COUNT;
|
||||
|
||||
// Due to use of physics, duration may differ between devices so we need to calculate and
|
||||
// cache the value.
|
||||
private int mHintToNormalDuration = -1;
|
||||
|
||||
public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
|
||||
|
||||
public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
|
||||
|
@ -221,6 +226,14 @@ public class QuickstepAtomicAnimationFactory extends
|
|||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
|
||||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
|
||||
config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
} else if (fromState == HINT_STATE && toState == NORMAL) {
|
||||
config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
|
||||
if (mHintToNormalDuration == -1) {
|
||||
ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
|
||||
toState.getWorkspaceScaleAndTranslation(mActivity).scale);
|
||||
mHintToNormalDuration = (int) va.getDuration();
|
||||
}
|
||||
config.duration = Math.max(config.duration, mHintToNormalDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,15 @@
|
|||
*/
|
||||
package com.android.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
|
||||
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
|
||||
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
@ -41,6 +45,7 @@ import com.android.launcher3.anim.Interpolators;
|
|||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
|
@ -112,7 +117,8 @@ public class NavBarToHomeTouchController implements TouchController,
|
|||
}
|
||||
return true;
|
||||
}
|
||||
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
|
||||
int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
|
||||
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED,
|
||||
"NavBarToHomeTouchController.canInterceptTouch true 2 "
|
||||
|
@ -242,5 +248,9 @@ public class NavBarToHomeTouchController implements TouchController,
|
|||
startContainerType,
|
||||
mEndState.containerType,
|
||||
mLauncher.getWorkspace().getCurrentPage());
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
|
||||
.withDstState(StatsLogManager.containerTypeToAtomState(mEndState.containerType))
|
||||
.log(LAUNCHER_HOME_GESTURE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
|
|||
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
|
||||
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
|
@ -34,6 +36,7 @@ import com.android.launcher3.Launcher;
|
|||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
@ -59,9 +62,14 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
|
||||
private boolean mDidTouchStartInNavBar;
|
||||
private boolean mReachedOverview;
|
||||
private boolean mIsOverviewRehidden;
|
||||
private boolean mIsHomeStaggeredAnimFinished;
|
||||
// The last recorded displacement before we reached overview.
|
||||
private PointF mStartDisplacement = new PointF();
|
||||
|
||||
// Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
|
||||
private ObjectAnimator mNormalToHintOverviewScrimAnimator;
|
||||
|
||||
public NoButtonNavbarToOverviewTouchController(Launcher l) {
|
||||
super(l);
|
||||
mRecentsView = l.getOverviewPanel();
|
||||
|
@ -107,10 +115,30 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
@Override
|
||||
public void onDragStart(boolean start, float startDisplacement) {
|
||||
super.onDragStart(start, startDisplacement);
|
||||
|
||||
if (mFromState == NORMAL && mToState == HINT_STATE) {
|
||||
mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofFloat(
|
||||
mLauncher.getDragLayer().getOverviewScrim(),
|
||||
OverviewScrim.SCRIM_PROGRESS,
|
||||
mFromState.getOverviewScrimAlpha(mLauncher),
|
||||
mToState.getOverviewScrimAlpha(mLauncher));
|
||||
}
|
||||
mReachedOverview = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProgress(float fraction) {
|
||||
super.updateProgress(fraction);
|
||||
if (mNormalToHintOverviewScrimAnimator != null) {
|
||||
mNormalToHintOverviewScrimAnimator.setCurrentFraction(fraction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity) {
|
||||
super.onDragEnd(velocity);
|
||||
mNormalToHintOverviewScrimAnimator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
|
||||
LauncherState targetState, float velocity, boolean isFling) {
|
||||
|
@ -129,6 +157,7 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
if (mCurrentAnimation == null) {
|
||||
return;
|
||||
}
|
||||
mNormalToHintOverviewScrimAnimator = null;
|
||||
mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
|
||||
mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
|
||||
mReachedOverview = true;
|
||||
|
@ -144,6 +173,13 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
}
|
||||
}
|
||||
|
||||
// Used if flinging back to home after reaching overview
|
||||
private void maybeSwipeInteractionToHomeComplete() {
|
||||
if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) {
|
||||
onSwipeInteractionCompleted(NORMAL, Touch.FLING);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handlingOverviewAnim() {
|
||||
return mDidTouchStartInNavBar && super.handlingOverviewAnim();
|
||||
|
@ -180,9 +216,17 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
stateManager.goToState(NORMAL, true,
|
||||
() -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
|
||||
} else {
|
||||
mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false;
|
||||
|
||||
StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
|
||||
mLauncher, velocity, false /* animateOverviewScrim */);
|
||||
staggeredWorkspaceAnim.start();
|
||||
staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mIsHomeStaggeredAnimFinished = true;
|
||||
maybeSwipeInteractionToHomeComplete();
|
||||
}
|
||||
}).start();
|
||||
|
||||
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
|
||||
stateManager.cancelAnimation();
|
||||
|
@ -191,8 +235,10 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
|
|||
config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
|
||||
AnimatorSet anim = stateManager.createAtomicAnimation(
|
||||
stateManager.getState(), NORMAL, config);
|
||||
anim.addListener(AnimationSuccessListener.forRunnable(
|
||||
() -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE)));
|
||||
anim.addListener(AnimationSuccessListener.forRunnable(() -> {
|
||||
mIsOverviewRehidden = true;
|
||||
maybeSwipeInteractionToHomeComplete();
|
||||
}));
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
|
|||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
|
||||
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.getLauncherAtomEvent;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
|
||||
|
@ -61,6 +65,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
|||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.BothAxesSwipeDetector;
|
||||
|
@ -429,6 +434,13 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
|
|||
mStartState.containerType,
|
||||
targetState.containerType,
|
||||
mLauncher.getWorkspace().getCurrentPage());
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(LAUNCHER_STATE_HOME)
|
||||
.withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
|
||||
.log(getLauncherAtomEvent(mStartState.containerType, targetState.containerType,
|
||||
targetState.ordinal > mStartState.ordinal
|
||||
? LAUNCHER_UNKNOWN_SWIPEUP
|
||||
: LAUNCHER_UNKNOWN_SWIPEDOWN));
|
||||
mLauncher.getStateManager().goToState(targetState, false, this::clearState);
|
||||
}
|
||||
|
||||
|
|
|
@ -205,26 +205,30 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
|
|||
mRecentsAnimationController = recentsAnimationController;
|
||||
mRecentsAnimationTargets = targets;
|
||||
mTransformParams.setTargetSet(mRecentsAnimationTargets);
|
||||
DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
|
||||
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
|
||||
mGestureState.getRunningTaskId());
|
||||
|
||||
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
|
||||
Rect overviewStackBounds = mActivityInterface
|
||||
.getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
|
||||
dp = dp.getMultiWindowProfile(mContext,
|
||||
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
|
||||
} else {
|
||||
// If we are not in multi-window mode, home insets should be same as system insets.
|
||||
dp = dp.copy(mContext);
|
||||
}
|
||||
dp.updateInsets(targets.homeContentInsets);
|
||||
dp.updateIsSeascape(mContext);
|
||||
if (runningTaskTarget != null) {
|
||||
mTaskViewSimulator.setPreview(runningTaskTarget);
|
||||
}
|
||||
|
||||
initTransitionEndpoints(dp);
|
||||
// Only initialize the device profile, if it has not been initialized before, as in some
|
||||
// configurations targets.homeContentInsets may not be correct.
|
||||
if (mActivity == null) {
|
||||
DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
|
||||
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
|
||||
Rect overviewStackBounds = mActivityInterface
|
||||
.getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
|
||||
dp = dp.getMultiWindowProfile(mContext,
|
||||
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
|
||||
} else {
|
||||
// If we are not in multi-window mode, home insets should be same as system insets.
|
||||
dp = dp.copy(mContext);
|
||||
}
|
||||
dp.updateInsets(targets.homeContentInsets);
|
||||
dp.updateIsSeascape(mContext);
|
||||
initTransitionEndpoints(dp);
|
||||
}
|
||||
|
||||
// Notify when the animation starts
|
||||
if (!mRecentsAnimationStartCallbacks.isEmpty()) {
|
||||
|
|
|
@ -22,6 +22,12 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
|
|||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
|
||||
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.util.DefaultDisplay.getSingleFrameMs;
|
||||
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
|
||||
|
@ -62,6 +68,7 @@ import com.android.launcher3.Utilities;
|
|||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
|
@ -275,8 +282,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
|||
if (mActivity == activity) {
|
||||
return true;
|
||||
}
|
||||
mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
|
||||
if (mActivity != null) {
|
||||
// The launcher may have been recreated as a result of device rotation.
|
||||
int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
|
||||
|
@ -329,6 +335,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
|||
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
|
||||
return;
|
||||
}
|
||||
mTaskViewSimulator.setRecentsConfiguration(mActivity.getResources().getConfiguration());
|
||||
|
||||
// If we've already ended the gesture and are going home, don't prepare recents UI,
|
||||
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
|
||||
|
@ -891,6 +898,27 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|
|||
ContainerType.NAVBAR, ContainerType.APP,
|
||||
endTarget.containerType,
|
||||
pageIndex);
|
||||
StatsLogManager.EventEnum event;
|
||||
switch (endTarget) {
|
||||
case HOME:
|
||||
event = LAUNCHER_HOME_GESTURE;
|
||||
break;
|
||||
case RECENTS:
|
||||
event = LAUNCHER_OVERVIEW_GESTURE;
|
||||
break;
|
||||
case LAST_TASK:
|
||||
case NEW_TASK:
|
||||
event = (mLogDirection == Direction.LEFT)
|
||||
? LAUNCHER_QUICKSWITCH_LEFT
|
||||
: LAUNCHER_QUICKSWITCH_RIGHT;
|
||||
break;
|
||||
default:
|
||||
event = IGNORE;
|
||||
}
|
||||
StatsLogManager.newInstance(mContext).logger()
|
||||
.withSrcState(LAUNCHER_STATE_BACKGROUND)
|
||||
.withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
|
||||
.log(event);
|
||||
}
|
||||
|
||||
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.quickstep;
|
||||
|
||||
import static android.content.Intent.EXTRA_STREAM;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
|
||||
|
@ -67,7 +68,9 @@ public class ImageActionsApi {
|
|||
|
||||
UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
|
||||
mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
|
||||
intentForUri.putExtra(EXTRA_STREAM, uri);
|
||||
intentForUri
|
||||
.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(EXTRA_STREAM, uri);
|
||||
return new Intent[]{intentForUri};
|
||||
}, TAG));
|
||||
|
||||
|
|
|
@ -120,9 +120,7 @@ public final class LauncherActivityInterface extends
|
|||
@Override
|
||||
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
|
||||
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
|
||||
((RecentsView) getCreatedActivity().getOverviewPanel())
|
||||
.setLayoutRotation(deviceState.getCurrentActiveRotation(),
|
||||
deviceState.getDisplayRotation());
|
||||
notifyRecentsOfOrientation(deviceState);
|
||||
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
|
||||
@Override
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
|
||||
|
|
|
@ -4,21 +4,18 @@ import android.app.Activity;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.testing.TestInformationHandler;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
|
||||
private final Context mContext;
|
||||
protected final Context mContext;
|
||||
|
||||
public QuickstepTestInformationHandler(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
@ -27,6 +24,15 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
|||
public Bundle call(String method) {
|
||||
final Bundle response = new Bundle();
|
||||
switch (method) {
|
||||
case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
|
||||
return getLauncherUIProperty(Bundle::putInt, l -> {
|
||||
final float progress = LauncherState.OVERVIEW.getVerticalProgress(l)
|
||||
- LauncherState.ALL_APPS.getVerticalProgress(l);
|
||||
final float distance = l.getAllAppsController().getShiftRange() * progress;
|
||||
return (int) distance;
|
||||
});
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
|
||||
final float swipeHeight =
|
||||
LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile);
|
||||
|
@ -47,26 +53,6 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
|||
Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
|
||||
ArrayList<String> taskBaseIntentComponents = new ArrayList<>();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
RecentsModel.INSTANCE.get(mContext).getTasks((tasks) -> {
|
||||
for (Task t : tasks) {
|
||||
taskBaseIntentComponents.add(
|
||||
t.key.baseIntent.getComponent().flattenToString());
|
||||
}
|
||||
latch.countDown();
|
||||
});
|
||||
try {
|
||||
latch.await(2, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
response.putStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
taskBaseIntentComponents);
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED: {
|
||||
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
|
||||
|
|
|
@ -55,7 +55,7 @@ import com.android.launcher3.util.Themes;
|
|||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.fallback.FallbackRecentsStateController;
|
||||
import com.android.quickstep.fallback.FallbackRecentsView;
|
||||
import com.android.quickstep.fallback.RecentsRootView;
|
||||
import com.android.quickstep.fallback.RecentsDragLayer;
|
||||
import com.android.quickstep.fallback.RecentsState;
|
||||
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
|
@ -78,7 +78,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
new ActivityTracker<>();
|
||||
|
||||
private Handler mUiHandler = new Handler(Looper.getMainLooper());
|
||||
private RecentsRootView mRecentsRootView;
|
||||
|
||||
private RecentsDragLayer mDragLayer;
|
||||
private FallbackRecentsView mFallbackRecentsView;
|
||||
private OverviewActionsView mActionsView;
|
||||
|
||||
|
@ -89,13 +90,14 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
/**
|
||||
* Init drag layer and overview panel views.
|
||||
*/
|
||||
protected void initViews() {
|
||||
setContentView(R.layout.fallback_recents_activity);
|
||||
mRecentsRootView = findViewById(R.id.drag_layer);
|
||||
protected void setupViews() {
|
||||
inflateRootView(R.layout.fallback_recents_activity);
|
||||
setContentView(getRootView());
|
||||
mDragLayer = findViewById(R.id.drag_layer);
|
||||
mFallbackRecentsView = findViewById(R.id.overview_panel);
|
||||
mActionsView = findViewById(R.id.overview_actions_view);
|
||||
|
||||
mRecentsRootView.recreateControllers();
|
||||
mDragLayer.recreateControllers();
|
||||
mFallbackRecentsView.init(mActionsView);
|
||||
}
|
||||
|
||||
|
@ -105,12 +107,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
|
||||
}
|
||||
|
||||
public void onRootViewSizeChanged() {
|
||||
if (isInMultiWindowMode()) {
|
||||
onHandleConfigChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
@ -130,7 +126,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
dispatchDeviceProfileChanged();
|
||||
|
||||
reapplyUi();
|
||||
mRecentsRootView.recreateControllers();
|
||||
mDragLayer.recreateControllers();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,19 +138,14 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
|
||||
// In case we are reusing IDP, create a copy so that we don't conflict with Launcher
|
||||
// activity.
|
||||
return (mRecentsRootView != null) && isInMultiWindowMode()
|
||||
return (mDragLayer != null) && isInMultiWindowMode()
|
||||
? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
|
||||
: dp.copy(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseDragLayer getDragLayer() {
|
||||
return mRecentsRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getRootView() {
|
||||
return mRecentsRootView;
|
||||
return mDragLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -252,7 +243,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
|
||||
mOldConfig = new Configuration(getResources().getConfiguration());
|
||||
initDeviceProfile();
|
||||
initViews();
|
||||
setupViews();
|
||||
|
||||
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
|
||||
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
|
||||
|
|
|
@ -19,6 +19,7 @@ package com.android.quickstep;
|
|||
import static android.view.Surface.ROTATION_0;
|
||||
|
||||
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
|
||||
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
@ -65,15 +66,24 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
|
|||
}
|
||||
}
|
||||
RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
|
||||
boolean canLauncherRotate = orientedState.canLauncherRotate();
|
||||
boolean canLauncherRotate = orientedState.canRecentsActivityRotate();
|
||||
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
|
||||
|
||||
// Add overview actions to the menu when in in-place rotate landscape mode.
|
||||
if (!canLauncherRotate && isInLandscape) {
|
||||
for (TaskShortcutFactory actionMenuOption : ACTION_MENU_OPTIONS) {
|
||||
SystemShortcut shortcut = actionMenuOption.getShortcut(activity, taskView);
|
||||
if (shortcut != null) {
|
||||
shortcuts.add(shortcut);
|
||||
// Add screenshot action to task menu.
|
||||
SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
|
||||
.getShortcut(activity, taskView);
|
||||
if (screenshotShortcut != null) {
|
||||
shortcuts.add(screenshotShortcut);
|
||||
}
|
||||
|
||||
// Add modal action only if display orientation is the same as the device orientation.
|
||||
if (orientedState.getDisplayRotation() == ROTATION_0) {
|
||||
SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
|
||||
.getShortcut(activity, taskView);
|
||||
if (modalShortcut != null) {
|
||||
shortcuts.add(modalShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +114,6 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
|
|||
TaskShortcutFactory.WELLBEING
|
||||
};
|
||||
|
||||
private static final TaskShortcutFactory[] ACTION_MENU_OPTIONS = new TaskShortcutFactory[]{
|
||||
TaskShortcutFactory.SCREENSHOT,
|
||||
TaskShortcutFactory.MODAL
|
||||
};
|
||||
|
||||
/**
|
||||
* Overlay on each task handling Overview Action Buttons.
|
||||
*/
|
||||
|
@ -139,8 +144,12 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
|
|||
/**
|
||||
* Called when the current task is interactive for the user
|
||||
*/
|
||||
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
|
||||
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
|
||||
boolean rotated) {
|
||||
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
|
||||
|
||||
mActionsView.updateDisabledFlags(DISABLED_ROTATED, rotated);
|
||||
|
||||
getActionsView().setCallbacks(new OverlayUICallbacks() {
|
||||
@Override
|
||||
public void onShare() {
|
||||
|
|
|
@ -196,7 +196,8 @@ public interface TaskShortcutFactory {
|
|||
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
|
||||
future, animStartedListener, mHandler, true /* scaleUp */,
|
||||
taskKey.displayId);
|
||||
mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.getItemInfo());
|
||||
mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
|
||||
.log(mLauncherEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,8 +297,8 @@ public interface TaskShortcutFactory {
|
|||
};
|
||||
mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
|
||||
dismissTaskMenuView(mTarget);
|
||||
mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
|
||||
mTaskView.getItemInfo());
|
||||
mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
|
||||
.log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -481,7 +481,6 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN");
|
||||
}
|
||||
mDeviceState.setOrientationTransformIfNeeded(event);
|
||||
GestureState newGestureState;
|
||||
|
||||
if (mDeviceState.isInSwipeUpTouchRegion(event)) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
|
@ -491,26 +490,25 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
|
||||
// onConsumerInactive and wipe the previous gesture state
|
||||
GestureState prevGestureState = new GestureState(mGestureState);
|
||||
newGestureState = createGestureState();
|
||||
mGestureState = createGestureState(mGestureState);
|
||||
mConsumer.onConsumerAboutToBeSwitched();
|
||||
mConsumer = newConsumer(prevGestureState, newGestureState, event);
|
||||
mConsumer = newConsumer(prevGestureState, mGestureState, event);
|
||||
|
||||
ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
|
||||
mUncheckedConsumer = mConsumer;
|
||||
} else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()) {
|
||||
newGestureState = createGestureState();
|
||||
ActivityManager.RunningTaskInfo runningTask = newGestureState.getRunningTask();
|
||||
mGestureState = createGestureState(mGestureState);
|
||||
ActivityManager.RunningTaskInfo runningTask = mGestureState.getRunningTask();
|
||||
if (mDeviceState.canTriggerAssistantAction(event, runningTask)) {
|
||||
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
|
||||
// should not interrupt it. QuickSwitch assumes that interruption can only
|
||||
// happen if the next gesture is also quick switch.
|
||||
mUncheckedConsumer = new AssistantInputConsumer(
|
||||
this,
|
||||
newGestureState,
|
||||
mGestureState,
|
||||
InputConsumer.NO_OP, mInputMonitorCompat,
|
||||
mOverviewComponentObserver.assistantGestureIsConstrained());
|
||||
} else {
|
||||
newGestureState = DEFAULT_STATE;
|
||||
mUncheckedConsumer = InputConsumer.NO_OP;
|
||||
}
|
||||
} else if (mDeviceState.canTriggerOneHandedAction(event)
|
||||
|
@ -518,14 +516,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
// Consume gesture event for triggering one handed feature.
|
||||
mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
|
||||
InputConsumer.NO_OP, mInputMonitorCompat);
|
||||
newGestureState = DEFAULT_STATE;
|
||||
} else {
|
||||
newGestureState = DEFAULT_STATE;
|
||||
mUncheckedConsumer = InputConsumer.NO_OP;
|
||||
}
|
||||
|
||||
// Save the current gesture state
|
||||
mGestureState = newGestureState;
|
||||
} else {
|
||||
// Other events
|
||||
if (mUncheckedConsumer != InputConsumer.NO_OP) {
|
||||
|
@ -535,7 +528,17 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
}
|
||||
|
||||
if (mUncheckedConsumer != InputConsumer.NO_OP) {
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
|
||||
switch (event.getActionMasked()) {
|
||||
case ACTION_DOWN:
|
||||
case ACTION_UP:
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent("
|
||||
+ (int) event.getRawX() + ", " + (int) event.getRawY() + ")",
|
||||
event.getActionMasked());
|
||||
break;
|
||||
default:
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
|
||||
|
@ -549,14 +552,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
|
||||
}
|
||||
|
||||
private GestureState createGestureState() {
|
||||
private GestureState createGestureState(GestureState previousGestureState) {
|
||||
GestureState gestureState = new GestureState(mOverviewComponentObserver,
|
||||
ActiveGestureLog.INSTANCE.generateAndSetLogId());
|
||||
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
|
||||
gestureState.updateRunningTask(mGestureState.getRunningTask());
|
||||
gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
|
||||
gestureState.updateRunningTask(previousGestureState.getRunningTask());
|
||||
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
|
||||
gestureState.updatePreviouslyAppearedTaskIds(
|
||||
mGestureState.getPreviouslyAppearedTaskIds());
|
||||
previousGestureState.getPreviouslyAppearedTaskIds());
|
||||
} else {
|
||||
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
|
||||
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
|
||||
|
@ -769,7 +772,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
|
||||
private void reset() {
|
||||
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
||||
mGestureState = new GestureState();
|
||||
mGestureState = DEFAULT_STATE;
|
||||
}
|
||||
|
||||
private void preloadOverview(boolean fromInit) {
|
||||
|
|
|
@ -106,12 +106,23 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldAddDummyTaskView(int runningTaskId) {
|
||||
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId
|
||||
&& getTaskViewCount() == 0) {
|
||||
// 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.
|
||||
return false;
|
||||
}
|
||||
return super.shouldAddDummyTaskView(runningTaskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyLoadPlan(ArrayList<Task> tasks) {
|
||||
// When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
|
||||
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
|
||||
// track the index of the next task appropriately, as if we are switching on any other app.
|
||||
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId) {
|
||||
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId && !tasks.isEmpty()) {
|
||||
// Check if the task list has running task
|
||||
boolean found = false;
|
||||
for (Task t : tasks) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.fallback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.RecentsActivity;
|
||||
|
||||
/**
|
||||
* Drag layer for fallback recents activity
|
||||
*/
|
||||
public class RecentsDragLayer extends BaseDragLayer<RecentsActivity> {
|
||||
|
||||
public RecentsDragLayer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 1 /* alphaChannelCount */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateControllers() {
|
||||
mControllers = new TouchController[] {
|
||||
new RecentsTaskController(mActivity),
|
||||
new FallbackNavBarTouchController(mActivity),
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
super.setInsets(insets);
|
||||
setBackground(insets.top == 0 || !mAllowSysuiScrims
|
||||
? null
|
||||
: Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.fallback;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.RecentsActivity;
|
||||
|
||||
public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
|
||||
|
||||
private static final int MIN_SIZE = 10;
|
||||
|
||||
private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
|
||||
|
||||
public RecentsRootView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 1 /* alphaChannelCount */);
|
||||
setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
|
||||
public Point getLastKnownSize() {
|
||||
return mLastKnownSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateControllers() {
|
||||
mControllers = new TouchController[] {
|
||||
new RecentsTaskController(mActivity),
|
||||
new FallbackNavBarTouchController(mActivity),
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// Check size changes before the actual measure, to avoid multiple measure calls.
|
||||
int width = Math.max(MIN_SIZE, MeasureSpec.getSize(widthMeasureSpec));
|
||||
int height = Math.max(MIN_SIZE, MeasureSpec.getSize(heightMeasureSpec));
|
||||
if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
|
||||
mLastKnownSize.set(width, height);
|
||||
mActivity.onRootViewSizeChanged();
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
@Override
|
||||
protected boolean fitSystemWindows(Rect insets) {
|
||||
// Update device profile before notifying the children.
|
||||
mActivity.getDeviceProfile().updateInsets(insets);
|
||||
setInsets(insets);
|
||||
return false; // Let children get the full insets
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
// If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
|
||||
// modifying child layout params.
|
||||
if (!insets.equals(mInsets)) {
|
||||
super.setInsets(insets);
|
||||
}
|
||||
setBackground(insets.top == 0 || !mAllowSysuiScrims
|
||||
? null
|
||||
: Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
|
||||
}
|
||||
|
||||
public void dispatchInsets() {
|
||||
mActivity.getDeviceProfile().updateInsets(mInsets);
|
||||
super.setInsets(mInsets);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ import static android.view.MotionEvent.ACTION_POINTER_UP;
|
|||
import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.MotionEvent.INVALID_POINTER_ID;
|
||||
|
||||
import static com.android.launcher3.PagedView.ACTION_MOVE_ALLOW_EASY_FLING;
|
||||
import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
|
||||
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
|
||||
|
@ -38,6 +40,7 @@ import android.graphics.PointF;
|
|||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
@ -76,7 +79,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
private static final String UP_EVT = "OtherActivityInputConsumer.UP";
|
||||
|
||||
// TODO: Move to quickstep contract
|
||||
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
|
||||
public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
|
||||
public static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
|
||||
|
||||
private final RecentsAnimationDeviceState mDeviceState;
|
||||
private final NavBarPosition mNavBarPosition;
|
||||
|
@ -150,10 +154,12 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
|
||||
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
|
||||
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
|
||||
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
|
||||
|
||||
float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
|
||||
mSquaredTouchSlop = slop * slop;
|
||||
float slopMultiplier = mDeviceState.isFullyGesturalNavMode()
|
||||
? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
|
||||
: QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
|
||||
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
|
||||
mSquaredTouchSlop = slopMultiplier * mTouchSlop * mTouchSlop;
|
||||
|
||||
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
|
||||
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
|
||||
|
@ -187,6 +193,10 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
&& !mRecentsViewDispatcher.hasConsumer()) {
|
||||
mRecentsViewDispatcher.setConsumer(mInteractionHandler
|
||||
.getRecentsViewDispatcher(mNavBarPosition.getRotation()));
|
||||
int action = ev.getAction();
|
||||
ev.setAction(ACTION_MOVE_ALLOW_EASY_FLING);
|
||||
mRecentsViewDispatcher.dispatchEvent(ev);
|
||||
ev.setAction(action);
|
||||
}
|
||||
int edgeFlags = ev.getEdgeFlags();
|
||||
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
|
||||
|
@ -317,6 +327,13 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
}
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP: {
|
||||
if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) {
|
||||
float displacementX = mLastPos.x - mDownPos.x;
|
||||
float displacementY = mLastPos.y - mDownPos.y;
|
||||
Log.d("Quickswitch", "mPassedWindowMoveSlop=false"
|
||||
+ " disp=" + squaredHypot(displacementX, displacementY)
|
||||
+ " slop=" + mSquaredTouchSlop);
|
||||
}
|
||||
finishTouchTracking(ev);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package com.android.quickstep.inputconsumers;
|
||||
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
|
@ -22,9 +25,10 @@ import android.view.MotionEvent;
|
|||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.logging.StatsLogUtils;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.quickstep.GestureState;
|
||||
|
@ -40,11 +44,13 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
|||
private final Context mContext;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
|
||||
private final GestureState mGestureState;
|
||||
|
||||
public OverviewWithoutFocusInputConsumer(Context context,
|
||||
RecentsAnimationDeviceState deviceState, GestureState gestureState,
|
||||
InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
|
||||
mContext = context;
|
||||
mGestureState = gestureState;
|
||||
mInputMonitor = inputMonitor;
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe,
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
|
||||
|
@ -81,10 +87,21 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
|||
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
|
||||
int pageIndex = -1; // This number doesn't reflect workspace page index.
|
||||
// It only indicates that launcher client screen was shown.
|
||||
int containerType = StatsLogUtils.getContainerTypeFromState(activity.getCurrentState());
|
||||
int containerType = (mGestureState != null && mGestureState.getEndTarget() != null)
|
||||
? mGestureState.getEndTarget().containerType
|
||||
: LauncherLogProto.ContainerType.WORKSPACE;
|
||||
activity.getUserEventDispatcher().logActionOnContainer(
|
||||
wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex);
|
||||
activity.getUserEventDispatcher().setPreviousHomeGesture(true);
|
||||
activity.getStatsLogManager().logger()
|
||||
.withSrcState(LAUNCHER_STATE_HOME)
|
||||
.withDstState(LAUNCHER_STATE_HOME)
|
||||
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(-1))
|
||||
.build())
|
||||
.log(LAUNCHER_HOME_GESTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -175,6 +175,11 @@ public class StaggeredWorkspaceAnim {
|
|||
return mAnimators;
|
||||
}
|
||||
|
||||
public StaggeredWorkspaceAnim addAnimatorListener(Animator.AnimatorListener listener) {
|
||||
mAnimators.addListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation.
|
||||
*/
|
||||
|
|
|
@ -22,6 +22,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_
|
|||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
|
@ -124,6 +125,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
mLayoutValid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration)
|
||||
*/
|
||||
public void setRecentsConfiguration(Configuration configuration) {
|
||||
mOrientationState.setActivityConfiguration(configuration);
|
||||
mLayoutValid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.android.quickstep.views.RecentsView#FULLSCREEN_PROGRESS
|
||||
*/
|
||||
|
@ -141,7 +150,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
*/
|
||||
public void setPreview(RemoteAnimationTargetCompat runningTarget) {
|
||||
setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
|
||||
mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y);
|
||||
mRunningTargetWindowPosition.set(runningTarget.screenSpaceBounds.left,
|
||||
runningTarget.screenSpaceBounds.top);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,7 +214,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
public void applyWindowToHomeRotation(Matrix matrix) {
|
||||
mMatrix.postTranslate(mDp.windowX, mDp.windowY);
|
||||
postDisplayRotation(deltaRotation(
|
||||
mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
|
||||
mOrientationState.getRecentsActivityRotation(),
|
||||
mOrientationState.getDisplayRotation()),
|
||||
mDp.widthPx, mDp.heightPx, matrix);
|
||||
matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y);
|
||||
}
|
||||
|
@ -225,7 +236,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
mPositionHelper.updateThumbnailMatrix(
|
||||
mThumbnailPosition, mThumbnailData,
|
||||
mTaskRect.width(), mTaskRect.height(),
|
||||
mDp, mOrientationState.getLauncherRotation());
|
||||
mDp, mOrientationState.getRecentsActivityRotation());
|
||||
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
|
||||
|
||||
PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
|
||||
|
@ -255,8 +266,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
float taskHeight = mTaskRect.height();
|
||||
|
||||
mMatrix.set(mPositionHelper.getMatrix());
|
||||
mMatrix.postScale(scale, scale);
|
||||
mMatrix.postTranslate(insets.left, insets.top);
|
||||
mMatrix.postScale(scale, scale);
|
||||
|
||||
// Apply TaskView matrix: translate, scale, scroll
|
||||
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
|
||||
|
|
|
@ -232,7 +232,7 @@ public class AllAppsEduView extends AbstractFloatingView {
|
|||
launcher, parent);
|
||||
view.init(launcher);
|
||||
launcher.getDragLayer().addView(view);
|
||||
launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
|
||||
launcher.getStatsLogManager().logger().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
|
||||
|
||||
view.requestLayout();
|
||||
view.playAnimation();
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.animation.ObjectAnimator;
|
|||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
|
@ -42,6 +43,7 @@ import com.android.launcher3.LauncherState;
|
|||
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.statemanager.StateManager.StateListener;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
|
@ -52,6 +54,7 @@ import com.android.quickstep.SysUINavigationMode;
|
|||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.RecentsExtraCard;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
/**
|
||||
* {@link RecentsView} used in Launcher activity
|
||||
|
@ -167,14 +170,21 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onTaskLaunched(boolean success) {
|
||||
protected void onTaskLaunchAnimationEnd(boolean success) {
|
||||
if (success) {
|
||||
mActivity.getStateManager().goToState(NORMAL, false /* animate */);
|
||||
} else {
|
||||
LauncherState state = mActivity.getStateManager().getState();
|
||||
mActivity.getAllAppsController().setState(state);
|
||||
}
|
||||
super.onTaskLaunched(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
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.content.Context;
|
|||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -39,6 +38,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
|||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -68,6 +68,15 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
|||
public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
|
||||
public static final int HIDDEN_NO_RECENTS = 1 << 5;
|
||||
|
||||
@IntDef(flag = true, value = {
|
||||
DISABLED_SCROLLING,
|
||||
DISABLED_ROTATED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ActionsDisabledFlags { }
|
||||
|
||||
public static final int DISABLED_SCROLLING = 1 << 0;
|
||||
public static final int DISABLED_ROTATED = 1 << 1;
|
||||
|
||||
private static final int INDEX_CONTENT_ALPHA = 0;
|
||||
private static final int INDEX_VISIBILITY_ALPHA = 1;
|
||||
private static final int INDEX_FULLSCREEN_ALPHA = 2;
|
||||
|
@ -78,6 +87,9 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
|||
@ActionsHiddenFlags
|
||||
private int mHiddenFlags;
|
||||
|
||||
@ActionsDisabledFlags
|
||||
protected int mDisabledFlags;
|
||||
|
||||
protected T mCallbacks;
|
||||
|
||||
public OverviewActionsView(Context context) {
|
||||
|
@ -117,7 +129,6 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
|||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mCallbacks == null) {
|
||||
Log.d("OverviewActionsView", "Callbacks null onClick");
|
||||
return;
|
||||
}
|
||||
int id = view.getId();
|
||||
|
@ -158,6 +169,25 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
|||
setVisibility(isHidden ? INVISIBLE : VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled.
|
||||
* Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable
|
||||
* buttons individually, currently done for select button in subclass.
|
||||
*
|
||||
* @param disabledFlags The flag to update.
|
||||
* @param enable Whether to enable the disable flag: True will cause view to be disabled.
|
||||
*/
|
||||
public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) {
|
||||
if (enable) {
|
||||
mDisabledFlags |= disabledFlags;
|
||||
} else {
|
||||
mDisabledFlags &= ~disabledFlags;
|
||||
}
|
||||
//
|
||||
boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
|
||||
LayoutUtils.setViewEnabled(this, isEnabled);
|
||||
}
|
||||
|
||||
public AlphaProperty getContentAlpha() {
|
||||
return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ import android.app.ActivityManager;
|
|||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
|
@ -105,6 +106,7 @@ import com.android.launcher3.anim.SpringProperty;
|
|||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
|
@ -146,8 +148,8 @@ import java.util.function.Consumer;
|
|||
* A list of recent tasks.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
|
||||
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
|
||||
public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
|
||||
Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
|
||||
InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
|
||||
SplitScreenBounds.OnChangeListener {
|
||||
|
||||
|
@ -208,7 +210,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
}
|
||||
};
|
||||
|
||||
protected final RecentsOrientedState mOrientationState;
|
||||
protected RecentsOrientedState mOrientationState;
|
||||
protected final BaseActivityInterface mSizeStrategy;
|
||||
protected RecentsAnimationController mRecentsAnimationController;
|
||||
protected RecentsAnimationTargets mRecentsAnimationTargets;
|
||||
|
@ -371,11 +373,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
|
||||
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
|
||||
(inMultiWindowMode) -> {
|
||||
if (!inMultiWindowMode && mOverviewStateEnabled) {
|
||||
// TODO: Re-enable layout transitions for addition of the unpinned task
|
||||
reloadIfNeeded();
|
||||
}
|
||||
};
|
||||
if (mOrientationState != null) {
|
||||
mOrientationState.setMultiWindowMode(inMultiWindowMode);
|
||||
setLayoutRotation(mOrientationState.getTouchRotation(),
|
||||
mOrientationState.getDisplayRotation());
|
||||
rotateAllChildTasks();
|
||||
}
|
||||
if (!inMultiWindowMode && mOverviewStateEnabled) {
|
||||
// TODO: Re-enable layout transitions for addition of the unpinned task
|
||||
reloadIfNeeded();
|
||||
}
|
||||
};
|
||||
|
||||
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
BaseActivityInterface sizeStrategy) {
|
||||
|
@ -383,12 +391,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
|
||||
setEnableFreeScroll(true);
|
||||
mSizeStrategy = sizeStrategy;
|
||||
mActivity = BaseActivity.fromContext(context);
|
||||
mOrientationState = new RecentsOrientedState(
|
||||
context, mSizeStrategy, this::animateRecentsRotationInPlace);
|
||||
mOrientationState.setActivityConfiguration(context.getResources().getConfiguration());
|
||||
|
||||
mFastFlingVelocity = getResources()
|
||||
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
|
||||
mActivity = BaseActivity.fromContext(context);
|
||||
mModel = RecentsModel.INSTANCE.get(context);
|
||||
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
|
||||
|
||||
|
@ -488,7 +497,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
return;
|
||||
}
|
||||
mModel.getIconCache().clear();
|
||||
reset();
|
||||
unloadVisibleTaskData();
|
||||
loadVisibleTaskData();
|
||||
}
|
||||
|
||||
public void init(OverviewActionsView actionsView) {
|
||||
|
@ -622,14 +632,14 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
@Override
|
||||
protected void onPageBeginTransition() {
|
||||
super.onPageBeginTransition();
|
||||
LayoutUtils.setViewEnabled(mActionsView, false);
|
||||
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPageEndTransition() {
|
||||
super.onPageEndTransition();
|
||||
if (isClearAllHidden()) {
|
||||
LayoutUtils.setViewEnabled(mActionsView, true);
|
||||
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
|
||||
}
|
||||
if (getNextPage() > 0) {
|
||||
setSwipeDownShouldLaunchApp(true);
|
||||
|
@ -791,8 +801,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
|
||||
TaskView taskView = getTaskViewAt(i);
|
||||
if (mIgnoreResetTaskId != taskView.getTask().key.id) {
|
||||
taskView.resetVisualProperties();
|
||||
taskView.resetViewTransforms();
|
||||
taskView.setStableAlpha(mContentAlpha);
|
||||
taskView.setFullscreenProgress(mFullscreenProgress);
|
||||
taskView.setModalness(mTaskModalness);
|
||||
}
|
||||
}
|
||||
if (mRunningTaskTileHidden) {
|
||||
|
@ -840,7 +852,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
|
||||
private void resetPaddingFromTaskSize() {
|
||||
DeviceProfile dp = mActivity.getDeviceProfile();
|
||||
mOrientationState.setMultiWindowMode(dp.isMultiWindowMode);
|
||||
getTaskSize(mTempRect);
|
||||
mTaskWidth = mTempRect.width();
|
||||
mTaskHeight = mTempRect.height();
|
||||
|
@ -992,6 +1003,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
mDwbToastShown = false;
|
||||
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
|
||||
LayoutUtils.setViewEnabled(mActionsView, true);
|
||||
mOrientationState.setGestureActive(false);
|
||||
}
|
||||
|
||||
public @Nullable TaskView getRunningTaskView() {
|
||||
|
@ -1029,6 +1041,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
*/
|
||||
public void onGestureAnimationStart(int runningTaskId) {
|
||||
// This needs to be called before the other states are set since it can create the task view
|
||||
mOrientationState.setGestureActive(true);
|
||||
showCurrentTask(runningTaskId);
|
||||
setEnableFreeScroll(false);
|
||||
setEnableDrawingLiveTile(false);
|
||||
|
@ -1052,9 +1065,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
}
|
||||
|
||||
private void animateRecentsRotationInPlace(int newRotation) {
|
||||
if (mOrientationState.canLauncherRotate()) {
|
||||
// Update the rotation but let system take care of the rotation animation
|
||||
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
|
||||
if (mOrientationState.canRecentsActivityRotate()) {
|
||||
// Let system take care of the rotation
|
||||
return;
|
||||
}
|
||||
AnimatorSet pa = setRecentsChangedOrientation(true);
|
||||
|
@ -1092,6 +1104,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
* Called when a gesture from an app has finished.
|
||||
*/
|
||||
public void onGestureAnimationEnd() {
|
||||
mOrientationState.setGestureActive(false);
|
||||
|
||||
setOnScrollChangeListener(null);
|
||||
setEnableFreeScroll(true);
|
||||
setEnableDrawingLiveTile(true);
|
||||
|
@ -1103,6 +1117,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
animateActionsViewIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should add a dummy taskView for the running task id
|
||||
*/
|
||||
protected boolean shouldAddDummyTaskView(int runningTaskId) {
|
||||
return getTaskView(runningTaskId) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
|
||||
*
|
||||
|
@ -1110,7 +1131,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
* is called. Also scrolls the view to this task.
|
||||
*/
|
||||
public void showCurrentTask(int runningTaskId) {
|
||||
if (getTaskView(runningTaskId) == null) {
|
||||
if (shouldAddDummyTaskView(runningTaskId)) {
|
||||
boolean wasEmpty = getChildCount() == 0;
|
||||
// Add an empty view for now until the task plan is loaded and applied
|
||||
final TaskView taskView = mTaskViewPool.getView();
|
||||
|
@ -1347,8 +1368,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
|
||||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
endState.logAction, Direction.UP, index, compKey);
|
||||
mActivity.getStatsLogManager().log(
|
||||
LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.getItemInfo());
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
|
||||
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1627,27 +1648,41 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (mOrientationState.setActivityConfiguration(newConfig)) {
|
||||
updateOrientationHandler();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLayoutRotation(int touchRotation, int displayRotation) {
|
||||
if (mOrientationState.update(touchRotation, displayRotation)) {
|
||||
mOrientationHandler = mOrientationState.getOrientationHandler();
|
||||
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
|
||||
setLayoutDirection(mIsRtl
|
||||
? View.LAYOUT_DIRECTION_RTL
|
||||
: View.LAYOUT_DIRECTION_LTR);
|
||||
mClearAllButton.setLayoutDirection(mIsRtl
|
||||
? View.LAYOUT_DIRECTION_LTR
|
||||
: View.LAYOUT_DIRECTION_RTL);
|
||||
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
|
||||
mActivity.getDragLayer().recreateControllers();
|
||||
boolean isInLandscape = touchRotation != 0
|
||||
|| mOrientationState.getLauncherRotation() != ROTATION_0;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
|
||||
!mOrientationState.canLauncherRotate() && isInLandscape);
|
||||
resetPaddingFromTaskSize();
|
||||
requestLayout();
|
||||
updateOrientationHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOrientationHandler() {
|
||||
mOrientationHandler = mOrientationState.getOrientationHandler();
|
||||
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
|
||||
setLayoutDirection(mIsRtl
|
||||
? View.LAYOUT_DIRECTION_RTL
|
||||
: View.LAYOUT_DIRECTION_LTR);
|
||||
mClearAllButton.setLayoutDirection(mIsRtl
|
||||
? View.LAYOUT_DIRECTION_LTR
|
||||
: View.LAYOUT_DIRECTION_RTL);
|
||||
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
|
||||
mActivity.getDragLayer().recreateControllers();
|
||||
boolean isInLandscape = mOrientationState.getTouchRotation() != 0
|
||||
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
|
||||
!mOrientationState.canRecentsActivityRotate() && isInLandscape);
|
||||
resetPaddingFromTaskSize();
|
||||
requestLayout();
|
||||
// Reapply the current page to update page scrolls.
|
||||
setCurrentPage(mCurrentPage);
|
||||
}
|
||||
|
||||
public RecentsOrientedState getPagedViewOrientedState() {
|
||||
return mOrientationState;
|
||||
}
|
||||
|
@ -1728,6 +1763,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
setPivotY(mTempPointF.y);
|
||||
setTaskModalness(mTaskModalness);
|
||||
updatePageOffsets();
|
||||
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
}
|
||||
|
||||
private void updatePageOffsets() {
|
||||
|
@ -1929,7 +1966,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
mPendingAnimation.addEndListener((endState) -> {
|
||||
if (endState.isSuccess) {
|
||||
Consumer<Boolean> onLaunchResult = (result) -> {
|
||||
onTaskLaunched(result);
|
||||
onTaskLaunchAnimationEnd(result);
|
||||
if (!result) {
|
||||
tv.notifyTaskLaunchFailed(TAG);
|
||||
}
|
||||
|
@ -1940,11 +1977,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
endState.logAction, Direction.DOWN, indexOfChild(tv),
|
||||
TaskUtils.getLaunchComponentKeyForTask(task.key));
|
||||
mActivity.getStatsLogManager().log(
|
||||
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.getItemInfo());
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
|
||||
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
|
||||
}
|
||||
} else {
|
||||
onTaskLaunched(false);
|
||||
onTaskLaunchAnimationEnd(false);
|
||||
}
|
||||
mPendingAnimation = null;
|
||||
});
|
||||
|
@ -1956,12 +1993,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
|
||||
public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
|
||||
|
||||
protected void onTaskLaunched(boolean success) {
|
||||
protected void onTaskLaunchAnimationEnd(boolean success) {
|
||||
if (success) {
|
||||
resetTaskVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when task activity is launched
|
||||
*/
|
||||
public void onTaskLaunched(Task task){ }
|
||||
|
||||
@Override
|
||||
protected void notifyPageSwitchListener(int prevPage) {
|
||||
super.notifyPageSwitchListener(prevPage);
|
||||
|
@ -2211,10 +2253,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
getCurrentPageTaskView().setModalness(modalness);
|
||||
}
|
||||
// Only show actions view when it's modal for in-place landscape mode.
|
||||
boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
|
||||
boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
|
||||
&& mOrientationState.getTouchRotation() != ROTATION_0;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
|
||||
LayoutUtils.setViewEnabled(mActionsView, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -357,10 +357,9 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
|
|||
}
|
||||
|
||||
private void updateOverlay() {
|
||||
// The overlay doesn't really work when the screenshot is rotated, so don't add it.
|
||||
if (mOverlayEnabled && !mPreviewPositionHelper.mIsOrientationChanged
|
||||
&& mBitmapShader != null && mThumbnailData != null) {
|
||||
mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix);
|
||||
if (mOverlayEnabled && mBitmapShader != null && mThumbnailData != null) {
|
||||
mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
|
||||
mPreviewPositionHelper.mIsOrientationChanged);
|
||||
} else {
|
||||
mOverlay.reset();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ 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.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
|
||||
.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -214,7 +215,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
|
||||
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
|
||||
mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, getItemInfo());
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
|
||||
.log(LAUNCHER_TASK_LAUNCH_TAP);
|
||||
});
|
||||
|
||||
mCurrentFullscreenParams = new FullscreenDrawParams(context);
|
||||
|
@ -383,6 +385,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
}
|
||||
}, resultCallbackHandler);
|
||||
}
|
||||
getRecentsView().onTaskLaunched(mTask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,7 +435,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
|
||||
} else {
|
||||
mMenuView = TaskMenuView.showForTask(this);
|
||||
mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, getItemInfo());
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
|
||||
.log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
|
||||
UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
|
||||
LauncherLogProto.ItemType.TASK_ICON);
|
||||
if (mMenuView != null) {
|
||||
|
@ -543,7 +547,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
setIconAndDimTransitionProgress(iconScale, invert);
|
||||
}
|
||||
|
||||
private void resetViewTransforms() {
|
||||
protected void resetViewTransforms() {
|
||||
setCurveScale(1);
|
||||
setTranslationX(0f);
|
||||
setTranslationY(0f);
|
||||
|
@ -552,12 +556,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
setIconScaleAndDim(1);
|
||||
}
|
||||
|
||||
public void resetVisualProperties() {
|
||||
resetViewTransforms();
|
||||
setFullscreenProgress(0);
|
||||
setModalness(0);
|
||||
}
|
||||
|
||||
public void setStableAlpha(float parentAlpha) {
|
||||
mStableAlpha = parentAlpha;
|
||||
setAlpha(mStableAlpha);
|
||||
|
@ -951,9 +949,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
*/
|
||||
public void setFullscreenProgress(float progress) {
|
||||
progress = Utilities.boundToRange(progress, 0, 1);
|
||||
if (progress == mFullscreenProgress) {
|
||||
return;
|
||||
}
|
||||
mFullscreenProgress = progress;
|
||||
boolean isFullscreen = mFullscreenProgress > 0;
|
||||
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
<string name="task_overlay_factory_class" translatable="false"/>
|
||||
|
||||
<!-- Activities which block home gesture -->
|
||||
<string-array name="gesture_blocking_activities" translatable="false"/>
|
||||
<string-array name="gesture_blocking_activities" translatable="false">
|
||||
<item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
|
||||
</string-array>
|
||||
|
||||
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
|
||||
<!-- tip shown when user migrates and predictions are enabled in hotseat -->
|
||||
<string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
|
||||
<!-- tip shown when hotseat edu is requested while predicions are disabled -->
|
||||
<!-- tip shown when hotseat edu is requested while predictions are disabled -->
|
||||
<string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
|
||||
|
||||
<!-- content description for hotseat items -->
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.shadows.LShadowDisplay;
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
import com.android.quickstep.LauncherActivityInterface;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.annotation.LooperMode.Mode;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowDisplayManager;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@LooperMode(Mode.PAUSED)
|
||||
public class TaskViewSimulatorTest {
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(1200, 2450)
|
||||
.withInsets(new Rect(0, 80, 0, 120))
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(1200, 2450)
|
||||
.withInsets(new Rect(55, 80, 55, 120))
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(2450, 1250)
|
||||
.withInsets(new Rect(0, 80, 0, 40))
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(2450, 1250)
|
||||
.withInsets(new Rect(0, 80, 120, 0))
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(2450, 1250)
|
||||
.withInsets(new Rect(55, 80, 55, 120))
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskProperlyScaled_landscape_rotated() {
|
||||
new TaskMatrixVerifier()
|
||||
.withLauncherSize(1200, 2450)
|
||||
.withInsets(new Rect(0, 80, 0, 120))
|
||||
.withAppBounds(
|
||||
new Rect(0, 0, 2450, 1200),
|
||||
new Rect(0, 80, 0, 120),
|
||||
Surface.ROTATION_90)
|
||||
.verifyNoTransforms();
|
||||
}
|
||||
|
||||
private static class TaskMatrixVerifier extends TransformParams {
|
||||
|
||||
private final Context mContext = RuntimeEnvironment.application;
|
||||
|
||||
private Rect mAppBounds = new Rect();
|
||||
private Rect mLauncherInsets = new Rect();
|
||||
|
||||
private Rect mAppInsets;
|
||||
|
||||
private int mAppRotation = -1;
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
TaskMatrixVerifier withLauncherSize(int width, int height) {
|
||||
ShadowDisplayManager.changeDisplay(DEFAULT_DISPLAY,
|
||||
String.format("w%sdp-h%sdp-mdpi", width, height));
|
||||
if (mAppBounds.isEmpty()) {
|
||||
mAppBounds.set(0, 0, width, height);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
TaskMatrixVerifier withInsets(Rect insets) {
|
||||
LShadowDisplay shadowDisplay = Shadow.extract(
|
||||
mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
|
||||
shadowDisplay.setInsets(insets);
|
||||
mLauncherInsets.set(insets);
|
||||
return this;
|
||||
}
|
||||
|
||||
TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
|
||||
mAppBounds.set(bounds);
|
||||
mAppInsets = insets;
|
||||
mAppRotation = appRotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
void verifyNoTransforms() {
|
||||
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext)
|
||||
.getDeviceProfile(mContext);
|
||||
mDeviceProfile.updateInsets(mLauncherInsets);
|
||||
|
||||
TaskViewSimulator tvs = new TaskViewSimulator(mContext,
|
||||
LauncherActivityInterface.INSTANCE);
|
||||
tvs.setDp(mDeviceProfile);
|
||||
|
||||
int launcherRotation = DefaultDisplay.INSTANCE.get(mContext).getInfo().rotation;
|
||||
if (mAppRotation < 0) {
|
||||
mAppRotation = launcherRotation;
|
||||
}
|
||||
tvs.setLayoutRotation(launcherRotation, mAppRotation);
|
||||
if (mAppInsets == null) {
|
||||
mAppInsets = new Rect(mLauncherInsets);
|
||||
}
|
||||
tvs.setPreviewBounds(mAppBounds, mAppInsets);
|
||||
|
||||
tvs.fullScreenProgress.value = 1;
|
||||
tvs.recentsViewScale.value = tvs.getFullScreenScale();
|
||||
tvs.apply(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
|
||||
SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
|
||||
proxy.onBuildTargetParams(builder, null, this);
|
||||
return new SurfaceParams[] {builder.build()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySurfaceParams(SurfaceParams[] params) {
|
||||
// Verify that the task position remains the same
|
||||
RectF newAppBounds = new RectF(mAppBounds);
|
||||
params[0].matrix.mapRect(newAppBounds);
|
||||
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
|
||||
|
||||
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AlmostSame extends TypeSafeMatcher<RectF> {
|
||||
|
||||
// Allow 1px error margin to account for float to int conversions
|
||||
private final float mError = 1f;
|
||||
private final Rect mExpected;
|
||||
|
||||
AlmostSame(Rect expected) {
|
||||
mExpected = expected;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchesSafely(RectF item) {
|
||||
return Math.abs(item.left - mExpected.left) < mError
|
||||
&& Math.abs(item.top - mExpected.top) < mError
|
||||
&& Math.abs(item.right - mExpected.right) < mError
|
||||
&& Math.abs(item.bottom - mExpected.bottom) < mError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendValue(mExpected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,6 +83,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
|
||||
addMultiWindowModeChangedListener(mDepthController);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,11 +20,15 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
|
|||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.os.IBinder;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
|
@ -41,7 +45,8 @@ import com.android.systemui.shared.system.WallpaperManagerCompat;
|
|||
/**
|
||||
* Controls blur and wallpaper zoom, for the Launcher surface only.
|
||||
*/
|
||||
public class DepthController implements StateHandler<LauncherState> {
|
||||
public class DepthController implements StateHandler<LauncherState>,
|
||||
BaseActivity.MultiWindowModeChangedListener {
|
||||
|
||||
public static final FloatProperty<DepthController> DEPTH =
|
||||
new FloatProperty<DepthController>("depth") {
|
||||
|
@ -104,6 +109,9 @@ public class DepthController implements StateHandler<LauncherState> {
|
|||
*/
|
||||
private float mDepth;
|
||||
|
||||
// Workaround for animating the depth when multiwindow mode changes.
|
||||
private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
|
||||
|
||||
private View.OnAttachStateChangeListener mOnAttachListener;
|
||||
|
||||
public DepthController(Launcher l) {
|
||||
|
@ -171,7 +179,7 @@ public class DepthController implements StateHandler<LauncherState> {
|
|||
|
||||
@Override
|
||||
public void setState(LauncherState toState) {
|
||||
if (mSurface == null) {
|
||||
if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -186,7 +194,8 @@ public class DepthController implements StateHandler<LauncherState> {
|
|||
PendingAnimation animation) {
|
||||
if (mSurface == null
|
||||
|| config.onlyPlayAtomicComponent()
|
||||
|| config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)) {
|
||||
|| config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
|
||||
|| mIgnoreStateChangesDuringMultiWindowAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -231,4 +240,21 @@ public class DepthController implements StateHandler<LauncherState> {
|
|||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
|
||||
mIgnoreStateChangesDuringMultiWindowAnimation = true;
|
||||
|
||||
ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, DEPTH,
|
||||
mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode))
|
||||
.setDuration(300);
|
||||
mwAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mIgnoreStateChangesDuringMultiWindowAnimation = false;
|
||||
}
|
||||
});
|
||||
mwAnimation.setAutoCancel(true);
|
||||
mwAnimation.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
package com.android.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
|
||||
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
|
@ -36,7 +38,6 @@ import android.util.Log;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
|
@ -124,7 +125,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) {
|
||||
if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -18,11 +18,11 @@ package com.android.quickstep.logging;
|
|||
|
||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
|
||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
|
||||
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
|
||||
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
|
||||
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
|
||||
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
|
||||
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__SRC_STATE__HOME;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
@ -30,9 +30,12 @@ import android.util.Log;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom.FolderIcon;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.InstanceIdSequence;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
|
@ -42,11 +45,15 @@ import com.android.launcher3.model.BgDataModel;
|
|||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.LogConfig;
|
||||
import com.android.systemui.shared.system.SysUiStatsLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
/**
|
||||
* This class calls StatsLog compile time generated methods.
|
||||
|
@ -73,75 +80,9 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
sContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a {@link EventEnum}.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event) {
|
||||
log(event, DEFAULT_INSTANCE_ID, (ItemInfo) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId}.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event, InstanceId instanceId) {
|
||||
log(event, instanceId, (ItemInfo) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event, @Nullable ItemInfo info) {
|
||||
log(event, DEFAULT_INSTANCE_ID, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param atomInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
|
||||
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
|
||||
new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel,
|
||||
AllAppsList apps) {
|
||||
write(event, instanceId, atomInfo, null,
|
||||
LAUNCHER_UICHANGED__DST_STATE__HOME,
|
||||
LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param atomItemInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo atomItemInfo, int srcState,
|
||||
int dstState) {
|
||||
write(event, DEFAULT_INSTANCE_ID,
|
||||
atomItemInfo == null ? LauncherAtom.ItemInfo.getDefaultInstance() : atomItemInfo,
|
||||
null,
|
||||
srcState,
|
||||
dstState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event, InstanceId instanceId,
|
||||
@Nullable ItemInfo info) {
|
||||
logInternal(event, instanceId, info,
|
||||
LAUNCHER_UICHANGED__DST_STATE__HOME,
|
||||
LAUNCHER_UICHANGED__DST_STATE__BACKGROUND,
|
||||
DEFAULT_PAGE_INDEX);
|
||||
public StatsLogger logger() {
|
||||
return new StatsCompatLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,102 +98,6 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
position /* position_picked = 4; */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link LauncherState}s. If either of the state refers
|
||||
* to workspace state, then use pageIndex to pass in index of workspace.
|
||||
*/
|
||||
@Override
|
||||
public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
|
||||
logInternal(event, DEFAULT_INSTANCE_ID, null, srcState, dstState, pageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
|
||||
*/
|
||||
private void logInternal(EventEnum event, InstanceId instanceId,
|
||||
@Nullable ItemInfo info, int srcState, int dstState, int pageIndex) {
|
||||
|
||||
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
|
||||
new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel,
|
||||
AllAppsList apps) {
|
||||
writeEvent(event, instanceId, info, srcState, dstState, pageIndex,
|
||||
dataModel.folders);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void writeEvent(EventEnum event, InstanceId instanceId,
|
||||
@Nullable ItemInfo info, int srcState, int dstState, int pageIndex,
|
||||
IntSparseArrayMap<FolderInfo> folders) {
|
||||
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
return;
|
||||
}
|
||||
LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
|
||||
if (info != null) {
|
||||
if (info.container >= 0) {
|
||||
atomInfo = info.buildProto(folders.get(info.container));
|
||||
} else {
|
||||
atomInfo = info.buildProto();
|
||||
}
|
||||
} else {
|
||||
if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
|
||||
|| dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
|
||||
atomInfo = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
|
||||
LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
|
||||
)).build();
|
||||
}
|
||||
}
|
||||
write(event, instanceId, atomInfo, info, srcState, dstState);
|
||||
}
|
||||
|
||||
private static void write(EventEnum event, InstanceId instanceId,
|
||||
LauncherAtom.ItemInfo atomInfo,
|
||||
@Nullable ItemInfo info,
|
||||
int srcState, int dstState) {
|
||||
if (IS_VERBOSE) {
|
||||
String name = (event instanceof Enum) ? ((Enum) event).name() :
|
||||
event.getId() + "";
|
||||
|
||||
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
|
||||
? String.format("\n%s (State:%s->%s) \n%s\n%s", name, getStateString(srcState),
|
||||
getStateString(dstState), info, atomInfo)
|
||||
: String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s\n%s", name,
|
||||
getStateString(srcState), getStateString(dstState), instanceId, info,
|
||||
atomInfo));
|
||||
}
|
||||
|
||||
SysUiStatsLog.write(
|
||||
SysUiStatsLog.LAUNCHER_EVENT,
|
||||
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
|
||||
srcState,
|
||||
dstState,
|
||||
null /* launcher extensions, deprecated */,
|
||||
false /* quickstep_enabled, deprecated */,
|
||||
event.getId() /* event_id */,
|
||||
atomInfo.getItemCase().getNumber() /* target_id */,
|
||||
instanceId.getId() /* instance_id TODO */,
|
||||
0 /* uid TODO */,
|
||||
getPackageName(atomInfo) /* package_name */,
|
||||
getComponentName(atomInfo) /* component_name */,
|
||||
getGridX(atomInfo, false) /* grid_x */,
|
||||
getGridY(atomInfo, false) /* grid_y */,
|
||||
getPageId(atomInfo, false) /* page_id */,
|
||||
getGridX(atomInfo, true) /* grid_x_parent */,
|
||||
getGridY(atomInfo, true) /* grid_y_parent */,
|
||||
getPageId(atomInfo, true) /* page_id_parent */,
|
||||
getHierarchy(atomInfo) /* hierarchy */,
|
||||
atomInfo.getIsWork() /* is_work_profile */,
|
||||
atomInfo.getRank() /* rank */,
|
||||
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
|
||||
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
|
||||
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
|
||||
getCardinality(atomInfo) /* cardinality */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the workspace layout information on the model thread.
|
||||
*/
|
||||
|
@ -279,10 +124,14 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
writeSnapshot(atomInfo, mInstanceId);
|
||||
}
|
||||
for (FolderInfo fInfo : folders) {
|
||||
for (ItemInfo info : fInfo.contents) {
|
||||
LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
|
||||
writeSnapshot(atomInfo, mInstanceId);
|
||||
}
|
||||
try {
|
||||
ArrayList<WorkspaceItemInfo> folderContents =
|
||||
(ArrayList) Executors.MAIN_EXECUTOR.submit(fInfo.contents::clone).get();
|
||||
for (ItemInfo info : folderContents) {
|
||||
LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
|
||||
writeSnapshot(atomInfo, mInstanceId);
|
||||
}
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
for (ItemInfo info : appWidgets) {
|
||||
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
|
||||
|
@ -299,7 +148,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
return;
|
||||
}
|
||||
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
|
||||
0 /* event_id */,
|
||||
LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
|
||||
info.getItemCase().getNumber() /* target_id */,
|
||||
instanceId.getId() /* instance_id */,
|
||||
0 /* uid */,
|
||||
|
@ -319,6 +168,173 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
info.getWidget().getSpanY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps to construct and write statsd compatible log message.
|
||||
*/
|
||||
private static class StatsCompatLogger implements StatsLogger {
|
||||
|
||||
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
|
||||
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
|
||||
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
|
||||
private OptionalInt mRank = OptionalInt.empty();
|
||||
private Optional<ContainerInfo> mContainerInfo = Optional.empty();
|
||||
private int mSrcState = LAUNCHER_STATE_UNSPECIFIED;
|
||||
private int mDstState = LAUNCHER_STATE_UNSPECIFIED;
|
||||
private Optional<FromState> mFromState = Optional.empty();
|
||||
private Optional<ToState> mToState = Optional.empty();
|
||||
private Optional<String> mEditText = Optional.empty();
|
||||
|
||||
@Override
|
||||
public StatsLogger withItemInfo(ItemInfo itemInfo) {
|
||||
if (mContainerInfo.isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
|
||||
}
|
||||
this.mItemInfo = itemInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withInstanceId(InstanceId instanceId) {
|
||||
this.mInstanceId = instanceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withRank(int rank) {
|
||||
this.mRank = OptionalInt.of(rank);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withSrcState(int srcState) {
|
||||
this.mSrcState = srcState;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withDstState(int dstState) {
|
||||
this.mDstState = dstState;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
|
||||
if (mItemInfo != DEFAULT_ITEM_INFO) {
|
||||
throw new IllegalArgumentException(
|
||||
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
|
||||
}
|
||||
this.mContainerInfo = Optional.of(containerInfo);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withFromState(FromState fromState) {
|
||||
this.mFromState = Optional.of(fromState);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withToState(ToState toState) {
|
||||
this.mToState = Optional.of(toState);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsLogger withEditText(String editText) {
|
||||
this.mEditText = Optional.of(editText);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(EventEnum event) {
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mItemInfo.container < 0) {
|
||||
// Item is not within a folder. Write to StatsLog in same thread.
|
||||
write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
|
||||
mDstState);
|
||||
} else {
|
||||
// Item is inside the folder, fetch folder info in a BG thread
|
||||
// and then write to StatsLog.
|
||||
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
|
||||
new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel,
|
||||
AllAppsList apps) {
|
||||
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
|
||||
write(event, mInstanceId,
|
||||
applyOverwrites(mItemInfo.buildProto(folderInfo)),
|
||||
mSrcState, mDstState);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
|
||||
LauncherAtom.ItemInfo.Builder itemInfoBuilder =
|
||||
(LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
|
||||
|
||||
mRank.ifPresent(itemInfoBuilder::setRank);
|
||||
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
|
||||
|
||||
if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
|
||||
FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
|
||||
.getFolderIcon()
|
||||
.toBuilder();
|
||||
mFromState.ifPresent(folderIconBuilder::setFromLabelState);
|
||||
mToState.ifPresent(folderIconBuilder::setToLabelState);
|
||||
mEditText.ifPresent(folderIconBuilder::setLabelInfo);
|
||||
itemInfoBuilder.setFolderIcon(folderIconBuilder);
|
||||
}
|
||||
return itemInfoBuilder.build();
|
||||
}
|
||||
|
||||
private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
|
||||
int srcState, int dstState) {
|
||||
if (IS_VERBOSE) {
|
||||
String name = (event instanceof Enum) ? ((Enum) event).name() :
|
||||
event.getId() + "";
|
||||
|
||||
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
|
||||
? String.format("\n%s (State:%s->%s)\n%s", name, getStateString(srcState),
|
||||
getStateString(dstState), atomInfo)
|
||||
: String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
|
||||
getStateString(srcState), getStateString(dstState), instanceId,
|
||||
atomInfo));
|
||||
}
|
||||
|
||||
SysUiStatsLog.write(
|
||||
SysUiStatsLog.LAUNCHER_EVENT,
|
||||
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
|
||||
srcState,
|
||||
dstState,
|
||||
null /* launcher extensions, deprecated */,
|
||||
false /* quickstep_enabled, deprecated */,
|
||||
event.getId() /* event_id */,
|
||||
atomInfo.getItemCase().getNumber() /* target_id */,
|
||||
instanceId.getId() /* instance_id TODO */,
|
||||
0 /* uid TODO */,
|
||||
getPackageName(atomInfo) /* package_name */,
|
||||
getComponentName(atomInfo) /* component_name */,
|
||||
getGridX(atomInfo, false) /* grid_x */,
|
||||
getGridY(atomInfo, false) /* grid_y */,
|
||||
getPageId(atomInfo, false) /* page_id */,
|
||||
getGridX(atomInfo, true) /* grid_x_parent */,
|
||||
getGridY(atomInfo, true) /* grid_y_parent */,
|
||||
getPageId(atomInfo, true) /* page_id_parent */,
|
||||
getHierarchy(atomInfo) /* hierarchy */,
|
||||
atomInfo.getIsWork() /* is_work_profile */,
|
||||
atomInfo.getRank() /* rank */,
|
||||
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
|
||||
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
|
||||
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
|
||||
getCardinality(atomInfo) /* cardinality */);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCardinality(LauncherAtom.ItemInfo info) {
|
||||
return info.getContainerInfo().getContainerCase().equals(PREDICTED_HOTSEAT_CONTAINER)
|
||||
? info.getContainerInfo().getPredictedHotseatContainer().getCardinality()
|
||||
|
|
|
@ -19,6 +19,7 @@ package com.android.quickstep.util;
|
|||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
|
||||
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.content.ClipData;
|
||||
|
@ -55,6 +56,9 @@ import java.util.function.Supplier;
|
|||
public class ImageActionUtils {
|
||||
|
||||
private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".overview.fileprovider";
|
||||
private static final long FILE_LIFE = 1000L /*ms*/ * 60L /*s*/ * 60L /*m*/ * 24L /*h*/;
|
||||
private static final String SUB_FOLDER = "Overview";
|
||||
private static final String BASE_NAME = "overview_image_";
|
||||
|
||||
/**
|
||||
* Saves screenshot to location determine by SystemUiProxy
|
||||
|
@ -88,8 +92,14 @@ public class ImageActionUtils {
|
|||
@WorkerThread
|
||||
public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop,
|
||||
Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
|
||||
context.startActivities(
|
||||
uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent));
|
||||
Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent);
|
||||
|
||||
// Work around b/159412574
|
||||
if (intents.length == 1) {
|
||||
context.startActivity(intents[0]);
|
||||
} else {
|
||||
context.startActivities(intents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,10 +114,13 @@ public class ImageActionUtils {
|
|||
*/
|
||||
@WorkerThread
|
||||
public static Uri getImageUri(Bitmap bitmap, Rect crop, Context context, String tag) {
|
||||
clearOldCacheFiles(context);
|
||||
Bitmap croppedBitmap = cropBitmap(bitmap, crop);
|
||||
int cropHash = crop == null ? 0 : crop.hashCode();
|
||||
String baseName = "image_" + bitmap.hashCode() + "_" + cropHash + ".png";
|
||||
File file = new File(context.getCacheDir(), baseName);
|
||||
String baseName = BASE_NAME + bitmap.hashCode() + "_" + cropHash + ".png";
|
||||
File parent = new File(context.getCacheDir(), SUB_FOLDER);
|
||||
parent.mkdir();
|
||||
File file = new File(parent, baseName);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||
|
@ -158,15 +171,30 @@ public class ImageActionUtils {
|
|||
intent = new Intent();
|
||||
}
|
||||
ClipData clipdata = new ClipData(new ClipDescription("content",
|
||||
new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
|
||||
new String[]{"image/png"}),
|
||||
new ClipData.Item(uri));
|
||||
intent.setAction(Intent.ACTION_SEND)
|
||||
.setComponent(null)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.setType("image/png")
|
||||
.setFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
.setClipData(clipdata);
|
||||
return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
|
||||
}
|
||||
|
||||
private static void clearOldCacheFiles(Context context) {
|
||||
THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
File parent = new File(context.getCacheDir(), SUB_FOLDER);
|
||||
File[] files = parent.listFiles((File f, String s) -> s.startsWith(BASE_NAME));
|
||||
if (files != null) {
|
||||
for (File file: files) {
|
||||
if (file.lastModified() + FILE_LIFE < System.currentTimeMillis()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,34 +57,16 @@ public class LayoutUtils {
|
|||
return shelfHeight + spaceBetweenShelfAndRecents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scale that should be applied to the TaskView so that it matches the target
|
||||
* TODO: Remove this method
|
||||
*/
|
||||
public static float getTaskScale(RecentsOrientedState orientedState,
|
||||
float srcWidth, float srcHeight, float targetWidth, float targetHeight) {
|
||||
if (orientedState == null
|
||||
|| orientedState.isHomeRotationAllowed()
|
||||
|| orientedState.isDisplayPhoneNatural()) {
|
||||
return srcWidth / targetWidth;
|
||||
} else {
|
||||
return srcHeight / targetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sets view and all children enabled/disabled.
|
||||
* @param viewGroup Top most parent view to change.
|
||||
* @param view Top most parent view to change.
|
||||
* @param enabled True = enable, False = disable.
|
||||
*/
|
||||
public static void setViewEnabled(ViewGroup viewGroup, boolean enabled) {
|
||||
viewGroup.setEnabled(enabled);
|
||||
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
if (child instanceof ViewGroup) {
|
||||
setViewEnabled((ViewGroup) child, enabled);
|
||||
} else {
|
||||
child.setEnabled(enabled);
|
||||
public static void setViewEnabled(View view, boolean enabled) {
|
||||
view.setEnabled(enabled);
|
||||
if (view instanceof ViewGroup) {
|
||||
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
|
||||
setViewEnabled(((ViewGroup) view).getChildAt(i), enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLaunc
|
|||
HotseatPredictionController client = mLauncher.getHotseatPredictionController();
|
||||
if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
|
||||
if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
|
||||
client.showDiscoveryTip();
|
||||
client.showEdu();
|
||||
stateManager.removeStateListener(this);
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLaunc
|
|||
@Override
|
||||
public void onStateTransitionComplete(LauncherState finalState) {
|
||||
if (finalState == NORMAL) {
|
||||
if (mCount == MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
|
||||
if (mCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
|
||||
if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
|
||||
AllAppsEduView.show(launcher);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Matrix;
|
||||
|
@ -47,6 +48,7 @@ import android.view.Surface;
|
|||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
|
@ -56,6 +58,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
|
|||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.systemui.shared.system.ConfigurationCompat;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.function.IntConsumer;
|
||||
|
@ -87,7 +90,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
|
||||
private @SurfaceRotation int mTouchRotation = ROTATION_0;
|
||||
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
|
||||
private @SurfaceRotation int mLauncherRotation = ROTATION_0;
|
||||
private @SurfaceRotation int mRecentsActivityRotation = ROTATION_0;
|
||||
|
||||
// Launcher activity supports multiple orientation, but fallback activity does not
|
||||
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
|
||||
|
@ -105,6 +108,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
|
||||
// Enable home rotation for UI tests, ignoring home rotation value from prefs
|
||||
private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 7;
|
||||
// Whether the swipe gesture is running, so the recents would stay locked in the
|
||||
// current orientation
|
||||
private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;
|
||||
|
||||
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
|
||||
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
|
||||
|
@ -114,22 +120,21 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
// multi-window is enabled as in that case, activity itself rotates.
|
||||
private static final int VALUE_ROTATION_WATCHER_ENABLED =
|
||||
MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
|
||||
| FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
|
||||
|
||||
private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
|
||||
newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
|
||||
| FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED
|
||||
| FLAG_SWIPE_UP_NOT_RUNNING;
|
||||
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final SharedPreferences mSharedPrefs;
|
||||
private final OrientationEventListener mOrientationListener;
|
||||
private final BaseActivityInterface mSizeStrategy;
|
||||
|
||||
private final Matrix mTmpMatrix = new Matrix();
|
||||
|
||||
private int mFlags;
|
||||
private int mPreviousRotation = ROTATION_0;
|
||||
|
||||
@Nullable private Configuration mActivityConfiguration;
|
||||
|
||||
/**
|
||||
* @param rotationChangeListener Callback for receiving rotation events when rotation watcher
|
||||
* is enabled
|
||||
|
@ -140,7 +145,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
mSharedPrefs = Utilities.getPrefs(context);
|
||||
mSizeStrategy = sizeStrategy;
|
||||
mOrientationListener = new OrientationEventListener(context) {
|
||||
@Override
|
||||
public void onOrientationChanged(int degrees) {
|
||||
|
@ -161,9 +165,19 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
if (originalSmallestWidth < 600) {
|
||||
mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
|
||||
}
|
||||
mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
|
||||
initFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration for the recents activity, which could affect the activity's rotation
|
||||
* @see #update(int, int)
|
||||
*/
|
||||
public boolean setActivityConfiguration(Configuration activityConfiguration) {
|
||||
mActivityConfiguration = activityConfiguration;
|
||||
return update(mTouchRotation, mDisplayRotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the host is in multi-window mode
|
||||
*/
|
||||
|
@ -171,6 +185,13 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
setFlag(FLAG_MULTIWINDOW_ROTATION_ALLOWED, isMultiWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the swipe up gesture is currently running or not
|
||||
*/
|
||||
public void setGestureActive(boolean isGestureActive) {
|
||||
setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
|
||||
* @param touchRotation The rotation the nav bar region that is touched is in
|
||||
|
@ -181,23 +202,19 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
*/
|
||||
public boolean update(
|
||||
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
|
||||
if (!isMultipleOrientationSupportedByDevice()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int launcherRotation = inferLauncherRotation(displayRotation);
|
||||
int recentsActivityRotation = inferRecentsActivityRotation(displayRotation);
|
||||
if (mDisplayRotation == displayRotation
|
||||
&& mTouchRotation == touchRotation
|
||||
&& mLauncherRotation == launcherRotation) {
|
||||
&& mRecentsActivityRotation == recentsActivityRotation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mLauncherRotation = launcherRotation;
|
||||
mRecentsActivityRotation = recentsActivityRotation;
|
||||
mDisplayRotation = displayRotation;
|
||||
mTouchRotation = touchRotation;
|
||||
mPreviousRotation = touchRotation;
|
||||
|
||||
if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
|
||||
if (mRecentsActivityRotation == mTouchRotation || canRecentsActivityRotate()) {
|
||||
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "current RecentsOrientedState: " + this);
|
||||
|
@ -219,9 +236,11 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
}
|
||||
|
||||
@SurfaceRotation
|
||||
private int inferLauncherRotation(@SurfaceRotation int displayRotation) {
|
||||
if (!isMultipleOrientationSupportedByDevice() || isHomeRotationAllowed()) {
|
||||
return displayRotation;
|
||||
private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
|
||||
if (isRecentsActivityRotationAllowed()) {
|
||||
return mActivityConfiguration == null
|
||||
? displayRotation
|
||||
: ConfigurationCompat.getWindowConfigurationRotation(mActivityConfiguration);
|
||||
} else {
|
||||
return ROTATION_0;
|
||||
}
|
||||
|
@ -229,7 +248,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
|
||||
private void setFlag(int mask, boolean enabled) {
|
||||
boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
|
||||
&& !canRecentsActivityRotate();
|
||||
if (enabled) {
|
||||
mFlags |= mask;
|
||||
} else {
|
||||
|
@ -237,7 +257,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
}
|
||||
|
||||
boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
|
||||
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
|
||||
&& !canRecentsActivityRotate();
|
||||
if (wasRotationEnabled != isRotationEnabled) {
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
if (isRotationEnabled) {
|
||||
|
@ -268,10 +289,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
|
||||
private void initFlags() {
|
||||
SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
|
||||
if (mOrientationListener.canDetectOrientation() &&
|
||||
currentMode != TWO_BUTTONS) {
|
||||
mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
|
||||
}
|
||||
boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
|
||||
currentMode != TWO_BUTTONS;
|
||||
setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
|
||||
|
||||
// initialize external flags
|
||||
updateAutoRotateSetting();
|
||||
|
@ -288,9 +308,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
mContentResolver.registerContentObserver(
|
||||
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
|
||||
false, mSystemAutoRotateObserver);
|
||||
SysUINavigationMode.Mode currentMode =
|
||||
SysUINavigationMode.INSTANCE.get(mContext)
|
||||
.addModeChangeListener(mNavModeChangeListener);
|
||||
}
|
||||
initFlags();
|
||||
}
|
||||
|
@ -302,8 +319,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
if (isMultipleOrientationSupportedByDevice()) {
|
||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
|
||||
SysUINavigationMode.INSTANCE.get(mContext)
|
||||
.removeModeChangeListener(mNavModeChangeListener);
|
||||
}
|
||||
setRotationWatcherEnabled(false);
|
||||
}
|
||||
|
@ -323,8 +338,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
}
|
||||
|
||||
@SurfaceRotation
|
||||
public int getLauncherRotation() {
|
||||
return mLauncherRotation;
|
||||
public int getRecentsActivityRotation() {
|
||||
return mRecentsActivityRotation;
|
||||
}
|
||||
|
||||
public boolean isMultipleOrientationSupportedByDevice() {
|
||||
|
@ -332,14 +347,21 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
== MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
|
||||
}
|
||||
|
||||
public boolean isHomeRotationAllowed() {
|
||||
return (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_MULTIWINDOW_ROTATION_ALLOWED))
|
||||
!= 0 ||
|
||||
(mFlags & FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING) != 0;
|
||||
public boolean isRecentsActivityRotationAllowed() {
|
||||
// Activity rotation is allowed if the multi-simulated-rotation is not supported
|
||||
// (fallback recents or tablets) or activity rotation is enabled by various settings.
|
||||
return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
|
||||
!= MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
|
||||
|| (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
|
||||
| FLAG_MULTIWINDOW_ROTATION_ALLOWED
|
||||
| FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
|
||||
}
|
||||
|
||||
public boolean canLauncherRotate() {
|
||||
return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isHomeRotationAllowed();
|
||||
/**
|
||||
* Returns true if the activity can rotate, if allowed by system rotation settings
|
||||
*/
|
||||
public boolean canRecentsActivityRotate() {
|
||||
return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isRecentsActivityRotationAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,20 +371,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled);
|
||||
}
|
||||
|
||||
public int getTouchRotationDegrees() {
|
||||
switch (mTouchRotation) {
|
||||
case ROTATION_90:
|
||||
return 90;
|
||||
case ROTATION_180:
|
||||
return 180;
|
||||
case ROTATION_270:
|
||||
return 270;
|
||||
case ROTATION_0:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scale and pivot so that the provided taskRect can fit the provided full size
|
||||
*/
|
||||
|
@ -521,8 +529,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
extractObjectNameAndAddress(mOrientationHandler.toString())
|
||||
+ " mDisplayRotation=" + mDisplayRotation
|
||||
+ " mTouchRotation=" + mTouchRotation
|
||||
+ " mLauncherRotation=" + mLauncherRotation
|
||||
+ " mHomeRotation=" + isHomeRotationAllowed()
|
||||
+ " mRecentsActivityRotation=" + mRecentsActivityRotation
|
||||
+ " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed()
|
||||
+ " mSystemRotation=" + systemRotationOn
|
||||
+ " mFlags=" + mFlags
|
||||
+ "]";
|
||||
|
@ -534,7 +542,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
public DeviceProfile getLauncherDeviceProfile() {
|
||||
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
|
||||
// TODO also check the natural orientation is landscape or portrait
|
||||
return (mLauncherRotation == ROTATION_90 || mLauncherRotation == ROTATION_270)
|
||||
return (mRecentsActivityRotation == ROTATION_90
|
||||
|| mRecentsActivityRotation == ROTATION_270)
|
||||
? idp.landscapeProfile
|
||||
: idp.portraitProfile;
|
||||
}
|
||||
|
|
|
@ -244,13 +244,17 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
|
|||
|
||||
@Override
|
||||
protected void updateSysUiColors() {
|
||||
// Use a light system UI (dark icons) if all apps is behind at least half of the
|
||||
// status bar.
|
||||
boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
|
||||
if (forceChange) {
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
|
||||
if (mDrawingFlatColor) {
|
||||
super.updateSysUiColors();
|
||||
} else {
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
|
||||
// Use a light system UI (dark icons) if all apps is behind at least half of the
|
||||
// status bar.
|
||||
boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
|
||||
if (forceChange) {
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
|
||||
} else {
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,9 @@ public class FallbackRecentsTest {
|
|||
TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
|
||||
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
|
||||
getLauncherCommand(getLauncherInMyProcess()));
|
||||
// b/143488140
|
||||
mDevice.pressHome();
|
||||
mDevice.waitForIdle();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -128,6 +131,10 @@ public class FallbackRecentsTest {
|
|||
TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
|
||||
getString("result"));
|
||||
}
|
||||
// b/143488140
|
||||
mDevice.pressHome();
|
||||
mDevice.waitForIdle();
|
||||
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
|
||||
}
|
||||
|
||||
// b/143488140
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
android:theme="@style/PopupItem" >
|
||||
|
||||
<com.android.launcher3.BubbleTextView
|
||||
style="@style/BaseIcon"
|
||||
style="@style/BaseIconUnBounded"
|
||||
android:id="@+id/bubble_text"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="start|center_vertical"
|
||||
|
@ -30,6 +30,7 @@
|
|||
android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
|
||||
android:paddingEnd="@dimen/popup_padding_end"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
launcher:iconDisplay="shortcut_popup"
|
||||
launcher:layoutHorizontal="true"
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/PrimaryMediumText"
|
||||
style="@style/PrimaryHeadline"
|
||||
android:id="@+id/work_mode_toggle"
|
||||
android:drawableStart="@drawable/ic_corp"
|
||||
android:drawablePadding="16dp"
|
||||
|
@ -25,6 +25,7 @@
|
|||
android:textColor="?attr/workProfileOverlayTextColor"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:ellipsize="end"
|
||||
android:elevation="10dp"
|
||||
android:gravity="start"
|
||||
android:lines="1"
|
||||
android:showText="false"
|
||||
|
|
|
@ -144,6 +144,10 @@
|
|||
<item name="staggered_stiffness" type="dimen" format="float">150</item>
|
||||
<dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
|
||||
|
||||
<item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
|
||||
<item name="hint_scale_stiffness" type="dimen" format="float">200</item>
|
||||
<dimen name="hint_scale_velocity_dp_per_s">0.3dp</dimen>
|
||||
|
||||
<!-- Swipe up to home related -->
|
||||
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
|
||||
<dimen name="swipe_up_y_overshoot">10dp</dimen>
|
||||
|
@ -175,6 +179,10 @@
|
|||
|
||||
<item>@dimen/swipe_up_fling_min_visible_change</item>
|
||||
<item>@dimen/swipe_up_y_overshoot</item>
|
||||
|
||||
<item>@dimen/hint_scale_damping_ratio</item>
|
||||
<item>@dimen/hint_scale_stiffness</item>
|
||||
<item>@dimen/hint_scale_velocity_dp_per_s</item>
|
||||
</array>
|
||||
|
||||
<string-array name="live_wallpapers_remove_sysui_scrims">
|
||||
|
|
|
@ -50,12 +50,12 @@
|
|||
<item name="folderFillColor">#CDFFFFFF</item>
|
||||
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
|
||||
<item name="folderTextColor">#FF212121</item>
|
||||
<item name="folderHintColor">#FF616161</item>
|
||||
<item name="folderHintColor">#89616161</item>
|
||||
<item name="loadingIconColor">#CCFFFFFF</item>
|
||||
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
|
||||
<item name="workProfileOverlayTextColor">#FF212121</item>
|
||||
<item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
|
||||
<item name="disabledIconAlpha">.36</item>
|
||||
<item name="disabledIconAlpha">.54</item>
|
||||
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
|
@ -69,7 +69,7 @@
|
|||
<style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
|
||||
<item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
|
||||
<item name="folderTextColor">?attr/workspaceTextColor</item>
|
||||
<item name="disabledIconAlpha">.24</item>
|
||||
<item name="disabledIconAlpha">.254</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
@ -106,7 +106,7 @@
|
|||
<item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
|
||||
<item name="folderIconBorderColor">#FF80868B</item>
|
||||
<item name="folderTextColor">@android:color/white</item>
|
||||
<item name="folderHintColor">#FFCCCCCC</item>
|
||||
<item name="folderHintColor">#89CCCCCC</item>
|
||||
<item name="isMainColorDark">true</item>
|
||||
<item name="loadingIconColor">#99FFFFFF</item>
|
||||
<item name="iconOnlyShortcutColor">#B3FFFFFF</item>
|
||||
|
@ -117,7 +117,7 @@
|
|||
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
|
||||
<item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
|
||||
<item name="folderTextColor">@android:color/white</item>
|
||||
<item name="disabledIconAlpha">.24</item>
|
||||
<item name="disabledIconAlpha">.54</item>
|
||||
</style>
|
||||
|
||||
<style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
|
||||
|
@ -187,28 +187,31 @@
|
|||
</style>
|
||||
|
||||
<style name="AllAppsTheme">
|
||||
<item name="disabledIconAlpha">.24</item>
|
||||
<item name="disabledIconAlpha">.54</item>
|
||||
</style>
|
||||
|
||||
<style name="AllAppsTheme.Dark">
|
||||
<item name="disabledIconAlpha">.28</item>
|
||||
<item name="disabledIconAlpha">.54</item>
|
||||
</style>
|
||||
|
||||
<!-- Base theme for BubbleTextView and sub classes -->
|
||||
<style name="BaseIcon" parent="@android:style/TextAppearance.DeviceDefault">
|
||||
|
||||
<style name="BaseIconUnBounded" parent="@android:style/TextAppearance.DeviceDefault">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:focusable">true</item>
|
||||
<item name="android:gravity">center_horizontal</item>
|
||||
<item name="android:lines">1</item>
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:defaultFocusHighlightEnabled">false</item>
|
||||
|
||||
<!-- No shadows in the base theme -->
|
||||
<item name="android:shadowRadius">0</item>
|
||||
</style>
|
||||
|
||||
<!-- Base theme for BubbleTextView and sub classes -->
|
||||
<style name="BaseIcon" parent="BaseIconUnBounded">
|
||||
<item name="android:lines">1</item>
|
||||
</style>
|
||||
|
||||
<!-- Icon displayed on the workspace -->
|
||||
<style name="BaseIcon.Workspace" >
|
||||
<item name="android:shadowRadius">2.0</item>
|
||||
|
@ -243,7 +246,6 @@
|
|||
<style name="DropTargetButton" parent="DropTargetButtonBase" />
|
||||
|
||||
<style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
|
||||
<style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
|
||||
<style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
|
||||
|
||||
<style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
|
||||
|
|
|
@ -5,6 +5,7 @@ shadows= \
|
|||
com.android.launcher3.shadows.LShadowAppWidgetManager \
|
||||
com.android.launcher3.shadows.LShadowBackupManager \
|
||||
com.android.launcher3.shadows.LShadowBitmap \
|
||||
com.android.launcher3.shadows.LShadowDisplay \
|
||||
com.android.launcher3.shadows.LShadowLauncherApps \
|
||||
com.android.launcher3.shadows.LShadowTypeface \
|
||||
com.android.launcher3.shadows.LShadowUserManager \
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.android.launcher3.folder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
@ -61,16 +62,16 @@ public final class FolderNameProviderTest {
|
|||
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
|
||||
list.add(mItem1);
|
||||
list.add(mItem2);
|
||||
FolderNameInfo[] nameInfos =
|
||||
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
|
||||
FolderNameInfos nameInfos = new FolderNameInfos();
|
||||
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
|
||||
assertEquals("Work", nameInfos[0].getLabel());
|
||||
assertEquals("Work", nameInfos.getLabels()[0]);
|
||||
|
||||
nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
|
||||
nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
|
||||
nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
|
||||
nameInfos.setLabel(0, "candidate1", 1.0f);
|
||||
nameInfos.setLabel(1, "candidate2", 1.0f);
|
||||
nameInfos.setLabel(2, "candidate3", 1.0f);
|
||||
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
|
||||
assertEquals("Work", nameInfos[3].getLabel());
|
||||
|
||||
assertEquals("Work", nameInfos.getLabels()[3]);
|
||||
assertTrue(nameInfos.hasSuggestions());
|
||||
assertTrue(nameInfos.hasPrimary());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.launcher3.shadows;
|
||||
|
||||
import static org.robolectric.shadow.api.Shadow.directlyOn;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.view.Display;
|
||||
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.RealObject;
|
||||
import org.robolectric.shadows.ShadowDisplay;
|
||||
|
||||
/**
|
||||
* Extension of {@link ShadowDisplay} with missing shadow methods
|
||||
*/
|
||||
@Implements(value = Display.class)
|
||||
public class LShadowDisplay extends ShadowDisplay {
|
||||
|
||||
private final Rect mInsets = new Rect();
|
||||
|
||||
@RealObject Display realObject;
|
||||
|
||||
/**
|
||||
* Sets the insets for the display
|
||||
*/
|
||||
public void setInsets(Rect insets) {
|
||||
mInsets.set(insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
|
||||
directlyOn(realObject, Display.class).getCurrentSizeRange(outSmallestSize, outLargestSize);
|
||||
outSmallestSize.x -= mInsets.left + mInsets.right;
|
||||
outLargestSize.x -= mInsets.left + mInsets.right;
|
||||
|
||||
outSmallestSize.y -= mInsets.top + mInsets.bottom;
|
||||
outLargestSize.y -= mInsets.top + mInsets.bottom;
|
||||
}
|
||||
}
|
|
@ -37,8 +37,6 @@ import androidx.annotation.IntDef;
|
|||
|
||||
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.StatsLogUtils;
|
||||
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.util.SystemUiController;
|
||||
|
@ -52,7 +50,7 @@ import java.util.ArrayList;
|
|||
/**
|
||||
* Launcher BaseActivity
|
||||
*/
|
||||
public abstract class BaseActivity extends Activity implements LogStateProvider, ActivityContext {
|
||||
public abstract class BaseActivity extends Activity implements ActivityContext {
|
||||
|
||||
private static final String TAG = "BaseActivity";
|
||||
|
||||
|
@ -146,13 +144,11 @@ public abstract class BaseActivity extends Activity implements LogStateProvider,
|
|||
return mDeviceProfile;
|
||||
}
|
||||
|
||||
public int getCurrentState() { return StatsLogUtils.LAUNCHER_STATE_BACKGROUND; }
|
||||
|
||||
public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
|
||||
|
||||
public final StatsLogManager getStatsLogManager() {
|
||||
if (mStatsLogManager == null) {
|
||||
mStatsLogManager = StatsLogManager.newInstance(this, this);
|
||||
mStatsLogManager = StatsLogManager.newInstance(this);
|
||||
}
|
||||
return mStatsLogManager;
|
||||
}
|
||||
|
|
|
@ -202,7 +202,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
|
|||
}
|
||||
|
||||
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
|
||||
getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, instanceId, info);
|
||||
getStatsLogManager().logger().withItemInfo(info).withInstanceId(instanceId)
|
||||
.log(LAUNCHER_APP_LAUNCH_TAP);
|
||||
}
|
||||
|
||||
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
|
||||
|
|
|
@ -126,11 +126,9 @@ public class DeleteDropTarget extends ButtonDropTarget {
|
|||
d.dragInfo.container = NO_ID;
|
||||
}
|
||||
super.onDrop(d, options);
|
||||
mStatsLogManager.log(
|
||||
mControlType == ControlType.REMOVE_TARGET
|
||||
? LAUNCHER_ITEM_DROPPED_ON_REMOVE
|
||||
: LAUNCHER_ITEM_DROPPED_ON_CANCEL,
|
||||
d.logInstanceId);
|
||||
mStatsLogManager.logger().withInstanceId(d.logInstanceId)
|
||||
.log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
|
||||
: LAUNCHER_ITEM_DROPPED_ON_CANCEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.android.launcher3.folder.FolderNameProvider;
|
|||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.InstanceIdSequence;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.util.Executors;
|
||||
|
||||
/**
|
||||
* Interface defining an object that can receive a drag.
|
||||
|
@ -84,7 +85,9 @@ public interface DropTarget {
|
|||
|
||||
public DragObject(Context context) {
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
folderNameProvider = FolderNameProvider.newInstance(context);
|
||||
Executors.MODEL_EXECUTOR.post(() -> {
|
||||
folderNameProvider = FolderNameProvider.newInstance(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ public class InvariantDeviceProfile {
|
|||
iconSize = p.iconSize;
|
||||
iconShapePath = p.iconShapePath;
|
||||
landscapeIconSize = p.landscapeIconSize;
|
||||
iconBitmapSize = p.iconBitmapSize;
|
||||
iconTextSize = p.iconTextSize;
|
||||
numHotseatIcons = p.numHotseatIcons;
|
||||
numAllAppsColumns = p.numAllAppsColumns;
|
||||
|
|
|
@ -38,6 +38,10 @@ import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
|||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState;
|
||||
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
|
||||
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
|
||||
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
|
||||
|
@ -110,8 +114,9 @@ import com.android.launcher3.folder.FolderIcon;
|
|||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.keyboard.CustomActionsPopup;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logging.FileLog;
|
||||
import com.android.launcher3.logging.StatsLogUtils;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.model.AppLaunchTracker;
|
||||
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||
|
@ -260,7 +265,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
|
||||
@Thunk
|
||||
Workspace mWorkspace;
|
||||
private View mLauncherView;
|
||||
@Thunk
|
||||
DragLayer mDragLayer;
|
||||
private DragController mDragController;
|
||||
|
@ -363,6 +367,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
LauncherAppState app = LauncherAppState.getInstance(this);
|
||||
mOldConfig = new Configuration(getResources().getConfiguration());
|
||||
mModel = app.getModel();
|
||||
|
||||
mRotationHelper = new RotationHelper(this);
|
||||
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
|
||||
initDeviceProfile(idp);
|
||||
|
@ -382,8 +387,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
appWidgetId -> getWorkspace().removeWidget(appWidgetId));
|
||||
mAppWidgetHost.startListening();
|
||||
|
||||
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
|
||||
|
||||
inflateRootView(R.layout.launcher);
|
||||
setupViews();
|
||||
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
|
||||
|
||||
|
@ -420,7 +424,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
// For handling default keys
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
setContentView(mLauncherView);
|
||||
setContentView(getRootView());
|
||||
getRootView().dispatchInsets();
|
||||
|
||||
// Listen for broadcasts
|
||||
|
@ -519,12 +523,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reapplyUi(boolean cancelCurrentAnimation) {
|
||||
getRootView().dispatchInsets();
|
||||
super.reapplyUi(cancelCurrentAnimation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
|
||||
onIdpChanged(idp);
|
||||
|
@ -581,11 +579,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
return mStateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends View> T findViewById(int id) {
|
||||
return mLauncherView.findViewById(id);
|
||||
}
|
||||
|
||||
private LauncherCallbacks mLauncherCallbacks;
|
||||
|
||||
/**
|
||||
|
@ -932,13 +925,32 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
|
||||
|
||||
private void logStopAndResume(int command) {
|
||||
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
|
||||
int containerType = mStateManager.getState().containerType;
|
||||
|
||||
StatsLogManager.EventEnum event;
|
||||
StatsLogManager.StatsLogger logger = getStatsLogManager().logger();
|
||||
if (command == Action.Command.RESUME) {
|
||||
logger.withSrcState(LAUNCHER_STATE_BACKGROUND)
|
||||
.withDstState(containerTypeToAtomState(mStateManager.getState().containerType));
|
||||
event = LAUNCHER_ONRESUME;
|
||||
} else { /* command == Action.Command.STOP */
|
||||
logger.withSrcState(containerTypeToAtomState(mStateManager.getState().containerType))
|
||||
.withDstState(LAUNCHER_STATE_BACKGROUND);
|
||||
event = LAUNCHER_ONSTOP;
|
||||
}
|
||||
|
||||
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
|
||||
getUserEventDispatcher().logActionCommand(command,
|
||||
containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
|
||||
containerType, -1, pageIndex);
|
||||
logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(pageIndex)).build());
|
||||
} else {
|
||||
getUserEventDispatcher().logActionCommand(command, containerType, -1);
|
||||
}
|
||||
logger.log(event);
|
||||
}
|
||||
|
||||
private void scheduleDeferredCheck() {
|
||||
|
@ -1118,10 +1130,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
mHotseat = findViewById(R.id.hotseat);
|
||||
mHotseat.setWorkspace(mWorkspace);
|
||||
|
||||
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
|
||||
// Setup the drag layer
|
||||
mDragLayer.setup(mDragController, mWorkspace);
|
||||
|
||||
|
@ -1334,11 +1342,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
return mAllAppsController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherRootView getRootView() {
|
||||
return (LauncherRootView) mLauncherView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragLayer getDragLayer() {
|
||||
return mDragLayer;
|
||||
|
@ -1856,16 +1859,6 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentState() {
|
||||
if (mStateManager.getState() == LauncherState.ALL_APPS) {
|
||||
return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
|
||||
} else if (mStateManager.getState() == OVERVIEW) {
|
||||
return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
|
||||
}
|
||||
return StatsLogUtils.LAUNCHER_STATE_HOME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
|
||||
@Nullable String sourceContainer) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import android.util.AttributeSet;
|
|||
import android.view.ViewDebug;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -17,7 +19,7 @@ public class LauncherRootView extends InsettableFrameLayout {
|
|||
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final StatefulActivity mActivity;
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
|
||||
|
@ -31,17 +33,17 @@ public class LauncherRootView extends InsettableFrameLayout {
|
|||
|
||||
public LauncherRootView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mActivity = StatefulActivity.fromContext(context);
|
||||
}
|
||||
|
||||
private void handleSystemWindowInsets(Rect insets) {
|
||||
// Update device profile before notifying th children.
|
||||
mLauncher.getDeviceProfile().updateInsets(insets);
|
||||
mActivity.getDeviceProfile().updateInsets(insets);
|
||||
boolean resetState = !insets.equals(mInsets);
|
||||
setInsets(insets);
|
||||
|
||||
if (resetState) {
|
||||
mLauncher.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
|
||||
mActivity.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +65,7 @@ public class LauncherRootView extends InsettableFrameLayout {
|
|||
}
|
||||
|
||||
public void dispatchInsets() {
|
||||
mLauncher.getDeviceProfile().updateInsets(mInsets);
|
||||
mActivity.getDeviceProfile().updateInsets(mInsets);
|
||||
super.setInsets(mInsets);
|
||||
}
|
||||
|
||||
|
|
|
@ -226,7 +226,16 @@ public abstract class LauncherState implements BaseState<LauncherState> {
|
|||
* 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
|
||||
*/
|
||||
public final float getDepth(Context context) {
|
||||
if (BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode) {
|
||||
return getDepth(context,
|
||||
BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
|
||||
* @see #getDepth(Context).
|
||||
*/
|
||||
public final float getDepth(Context context, boolean isMultiWindowMode) {
|
||||
if (isMultiWindowMode) {
|
||||
return 0;
|
||||
}
|
||||
return getDepthUnchecked(context);
|
||||
|
|
|
@ -71,7 +71,9 @@ import java.util.ArrayList;
|
|||
public abstract class PagedView<T extends View & PageIndicator> extends ViewGroup {
|
||||
private static final String TAG = "PagedView";
|
||||
private static final boolean DEBUG = false;
|
||||
public static final boolean DEBUG_FAILED_QUICKSWITCH = false;
|
||||
|
||||
public static final int ACTION_MOVE_ALLOW_EASY_FLING = MotionEvent.ACTION_MASK - 1;
|
||||
public static final int INVALID_PAGE = -1;
|
||||
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
|
||||
|
||||
|
@ -89,14 +91,16 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
// The following constants need to be scaled based on density. The scaled versions will be
|
||||
// assigned to the corresponding member variables below.
|
||||
private static final int FLING_THRESHOLD_VELOCITY = 500;
|
||||
private static final int EASY_FLING_THRESHOLD_VELOCITY = 400;
|
||||
private static final int MIN_SNAP_VELOCITY = 1500;
|
||||
private static final int MIN_FLING_VELOCITY = 250;
|
||||
|
||||
private boolean mFreeScroll = false;
|
||||
|
||||
protected int mFlingThresholdVelocity;
|
||||
protected int mMinFlingVelocity;
|
||||
protected int mMinSnapVelocity;
|
||||
protected final int mFlingThresholdVelocity;
|
||||
protected final int mEasyFlingThresholdVelocity;
|
||||
protected final int mMinFlingVelocity;
|
||||
protected final int mMinSnapVelocity;
|
||||
|
||||
protected boolean mFirstLayout = true;
|
||||
|
||||
|
@ -118,12 +122,17 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
private float mLastMotion;
|
||||
private float mLastMotionRemainder;
|
||||
private float mTotalMotion;
|
||||
// Used in special cases where the fling checks can be relaxed for an intentional gesture
|
||||
private boolean mAllowEasyFling;
|
||||
protected PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT;
|
||||
|
||||
protected int[] mPageScrolls;
|
||||
private boolean mIsBeingDragged;
|
||||
|
||||
// The amount of movement to begin scrolling
|
||||
protected int mTouchSlop;
|
||||
// The amount of movement to begin paging
|
||||
protected int mPageSlop;
|
||||
private int mMaximumVelocity;
|
||||
protected boolean mAllowOverScroll = true;
|
||||
|
||||
|
@ -170,24 +179,19 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
|
||||
setHapticFeedbackEnabled(false);
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes various states for this workspace.
|
||||
*/
|
||||
protected void init() {
|
||||
Context context = getContext();
|
||||
mScroller = new OverScroller(context);
|
||||
setDefaultInterpolator(Interpolators.SCROLL);
|
||||
mCurrentPage = 0;
|
||||
|
||||
final ViewConfiguration configuration = ViewConfiguration.get(context);
|
||||
mTouchSlop = configuration.getScaledPagingTouchSlop();
|
||||
mTouchSlop = configuration.getScaledTouchSlop();
|
||||
mPageSlop = configuration.getScaledPagingTouchSlop();
|
||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
||||
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
|
||||
mEasyFlingThresholdVelocity = (int) (EASY_FLING_THRESHOLD_VELOCITY * density);
|
||||
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
|
||||
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
|
||||
|
||||
|
@ -913,6 +917,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
|
||||
mLastMotionRemainder = 0;
|
||||
mTotalMotion = 0;
|
||||
mAllowEasyFling = false;
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
|
||||
updateIsBeingDraggedOnTouchDown();
|
||||
|
@ -944,7 +949,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
private void updateIsBeingDraggedOnTouchDown() {
|
||||
// mScroller.isFinished should be false when being flinged.
|
||||
final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
|
||||
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
|
||||
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mPageSlop / 3);
|
||||
|
||||
if (finishedScrolling) {
|
||||
mIsBeingDragged = false;
|
||||
|
@ -977,7 +982,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, pointerIndex);
|
||||
final int diff = (int) Math.abs(primaryDirection - mLastMotion);
|
||||
final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
|
||||
boolean moved = diff > touchSlop;
|
||||
boolean moved = diff > touchSlop || ev.getAction() == ACTION_MOVE_ALLOW_EASY_FLING;
|
||||
|
||||
if (moved) {
|
||||
// Scroll if the user moved far enough along the X axis
|
||||
|
@ -1160,6 +1165,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
|
||||
mLastMotionRemainder = 0;
|
||||
mTotalMotion = 0;
|
||||
mAllowEasyFling = false;
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
if (mIsBeingDragged) {
|
||||
onScrollInteractionBegin();
|
||||
|
@ -1167,8 +1173,14 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mIsBeingDragged) {
|
||||
case ACTION_MOVE_ALLOW_EASY_FLING:
|
||||
// Start scrolling immediately
|
||||
determineScrollingStart(ev);
|
||||
mAllowEasyFling = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mIsBeingDragged) {
|
||||
// Scroll to follow the motion event
|
||||
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
|
||||
|
@ -1214,9 +1226,14 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
SIGNIFICANT_MOVE_THRESHOLD;
|
||||
|
||||
mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection);
|
||||
boolean isFling = mTotalMotion > mTouchSlop && shouldFlingForVelocity(velocity);
|
||||
boolean passedSlop = mAllowEasyFling || mTotalMotion > mPageSlop;
|
||||
boolean isFling = passedSlop && shouldFlingForVelocity(velocity);
|
||||
boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0;
|
||||
boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0;
|
||||
if (DEBUG_FAILED_QUICKSWITCH && !isFling && mAllowEasyFling) {
|
||||
Log.d("Quickswitch", "isFling=false vel=" + velocity
|
||||
+ " threshold=" + mEasyFlingThresholdVelocity);
|
||||
}
|
||||
|
||||
if (!mFreeScroll) {
|
||||
// In the case that the page is moved far to one direction and then is flung
|
||||
|
@ -1316,7 +1333,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
}
|
||||
|
||||
protected boolean shouldFlingForVelocity(int velocity) {
|
||||
return Math.abs(velocity) > mFlingThresholdVelocity;
|
||||
float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
|
||||
return Math.abs(velocity) > threshold;
|
||||
}
|
||||
|
||||
private void resetTouchState() {
|
||||
|
@ -1393,8 +1411,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
|||
}
|
||||
|
||||
private void onSecondaryPointerUp(MotionEvent ev) {
|
||||
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
|
||||
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int pointerIndex = ev.getActionIndex();
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// This was our active pointer going up. Choose a new
|
||||
|
|
|
@ -220,9 +220,11 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
|
|||
d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
|
||||
super.onDrop(d, options);
|
||||
if (mCurrentAccessibilityAction == UNINSTALL) {
|
||||
mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
|
||||
mStatsLogManager.logger().withInstanceId(d.logInstanceId)
|
||||
.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
|
||||
} else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
|
||||
mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
|
||||
mStatsLogManager.logger().withInstanceId(d.logInstanceId)
|
||||
.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,10 +350,12 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
|
|||
mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
|
||||
mDragObject.dragSource = mOriginal;
|
||||
mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
|
||||
mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
|
||||
mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
|
||||
.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED);
|
||||
} else {
|
||||
sendFailure();
|
||||
mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
|
||||
mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
|
||||
.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
|||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -79,6 +82,7 @@ import com.android.launcher3.folder.PreviewBackground;
|
|||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||
import com.android.launcher3.icons.BitmapRenderer;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
|
@ -415,11 +419,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
|
||||
// Always enter the spring loaded mode
|
||||
mLauncher.getStateManager().goToState(SPRING_LOADED);
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
|
||||
dragObject.logInstanceId,
|
||||
dragObject.originalDragInfo
|
||||
);
|
||||
mStatsLogManager.logger().withItemInfo(dragObject.dragInfo)
|
||||
.withInstanceId(dragObject.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
|
||||
}
|
||||
|
||||
public void deferRemoveExtraEmptyScreen() {
|
||||
|
@ -1001,6 +1003,15 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
if (!mOverlayShown) {
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
|
||||
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(LAUNCHER_STATE_HOME)
|
||||
.withDstState(LAUNCHER_STATE_HOME)
|
||||
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(0))
|
||||
.build())
|
||||
.log(LAUNCHER_SWIPELEFT);
|
||||
}
|
||||
mOverlayShown = true;
|
||||
// Not announcing the overlay page for accessibility since it announces itself.
|
||||
|
@ -1010,6 +1021,15 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
if (!ued.isPreviousHomeGesture()) {
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
|
||||
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(LAUNCHER_STATE_HOME)
|
||||
.withDstState(LAUNCHER_STATE_HOME)
|
||||
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(-1))
|
||||
.build())
|
||||
.log(LAUNCHER_SWIPERIGHT);
|
||||
}
|
||||
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
|
||||
// When arriving to 0 overscroll from non-zero overscroll, announce page for
|
||||
|
@ -1101,9 +1121,20 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
protected void notifyPageSwitchListener(int prevPage) {
|
||||
super.notifyPageSwitchListener(prevPage);
|
||||
if (prevPage != mCurrentPage) {
|
||||
int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
|
||||
int swipeDirection = (prevPage < mCurrentPage)
|
||||
? Action.Direction.RIGHT : Action.Direction.LEFT;
|
||||
StatsLogManager.EventEnum event = (prevPage < mCurrentPage)
|
||||
? LAUNCHER_SWIPERIGHT : LAUNCHER_SWIPELEFT;
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
|
||||
swipeDirection, ContainerType.WORKSPACE, prevPage);
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(LAUNCHER_STATE_HOME)
|
||||
.withDstState(LAUNCHER_STATE_HOME)
|
||||
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(prevPage)).build())
|
||||
.log(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1646,10 +1677,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
Rect folderLocation = new Rect();
|
||||
float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
|
||||
target.removeView(v);
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
|
||||
d.logInstanceId,
|
||||
destInfo);
|
||||
mStatsLogManager.logger().withItemInfo(destInfo).withInstanceId(d.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED);
|
||||
FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
|
||||
targetCell[1]);
|
||||
destInfo.cellX = -1;
|
||||
|
@ -1687,10 +1716,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
if (dropOverView instanceof FolderIcon) {
|
||||
FolderIcon fi = (FolderIcon) dropOverView;
|
||||
if (fi.acceptDrop(d.dragInfo)) {
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
fi.mInfo);
|
||||
mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
|
||||
fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
|
||||
|
||||
// if the drag started here, we need to remove it from the workspace
|
||||
|
@ -1893,10 +1920,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
|
||||
mLauncher.getStateManager().goToState(
|
||||
NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo);
|
||||
mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
|
||||
}
|
||||
|
||||
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
|
||||
|
@ -2434,10 +2459,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
// widgets/shortcuts/folders in a slightly different way
|
||||
mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
|
||||
item.spanX, item.spanY);
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo);
|
||||
mStatsLogManager.logger().withItemInfo(d.dragInfo)
|
||||
.withInstanceId(d.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
|
||||
}
|
||||
};
|
||||
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|
||||
|
@ -2526,10 +2550,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
|
||||
resetTransitionTransform();
|
||||
}
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo);
|
||||
mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
|
||||
.log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
||||
|
@ -23,11 +25,13 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
|
|||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
|
||||
import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_HAS_BACKGROUNDS;
|
||||
import static com.android.launcher3.LauncherState.HINT_STATE;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
|
||||
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
|
||||
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
|
||||
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
|
||||
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
|
||||
|
@ -35,6 +39,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_F
|
|||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
|
@ -43,8 +48,11 @@ import com.android.launcher3.LauncherState.ScaleAndTranslation;
|
|||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringAnimationBuilder;
|
||||
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.util.DynamicResource;
|
||||
import com.android.systemui.plugins.ResourceProvider;
|
||||
|
||||
/**
|
||||
* Manages the animations between each of the workspace states.
|
||||
|
@ -104,17 +112,32 @@ public class WorkspaceStateTransitionAnimation {
|
|||
View qsbView = qsbScaleView.getSearchView();
|
||||
if (playAtomicComponent) {
|
||||
Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
|
||||
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
|
||||
LauncherState fromState = mLauncher.getStateManager().getState();
|
||||
boolean shouldSpring = propertySetter instanceof PendingAnimation
|
||||
&& fromState == HINT_STATE && state == NORMAL;
|
||||
if (shouldSpring) {
|
||||
((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
|
||||
mWorkspace, mNewScale));
|
||||
} else {
|
||||
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
|
||||
}
|
||||
|
||||
setPivotToScaleWithWorkspace(hotseat);
|
||||
setPivotToScaleWithWorkspace(qsbScaleView);
|
||||
float hotseatScale = hotseatScaleAndTranslation.scale;
|
||||
Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
|
||||
scaleInterpolator);
|
||||
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
|
||||
hotseatScaleInterpolator);
|
||||
propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
|
||||
hotseatScaleInterpolator);
|
||||
if (shouldSpring) {
|
||||
PendingAnimation pa = (PendingAnimation) propertySetter;
|
||||
pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
|
||||
pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
|
||||
qsbScaleAndTranslation.scale));
|
||||
} else {
|
||||
Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
|
||||
scaleInterpolator);
|
||||
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
|
||||
hotseatScaleInterpolator);
|
||||
propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
|
||||
hotseatScaleInterpolator);
|
||||
}
|
||||
|
||||
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
|
||||
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
|
||||
|
@ -191,4 +214,24 @@ public class WorkspaceStateTransitionAnimation {
|
|||
pageAlpha, fadeInterpolator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a spring based animator for the scale property of {@param v}.
|
||||
*/
|
||||
public static ValueAnimator getSpringScaleAnimator(Launcher launcher, View v, float scale) {
|
||||
ResourceProvider rp = DynamicResource.provider(launcher);
|
||||
float damping = rp.getFloat(R.dimen.hint_scale_damping_ratio);
|
||||
float stiffness = rp.getFloat(R.dimen.hint_scale_stiffness);
|
||||
float velocityPxPerS = rp.getDimension(R.dimen.hint_scale_velocity_dp_per_s);
|
||||
|
||||
return new SpringAnimationBuilder(v.getContext())
|
||||
.setStiffness(stiffness)
|
||||
.setDampingRatio(damping)
|
||||
.setMinimumVisibleChange(MIN_VISIBLE_CHANGE_SCALE)
|
||||
.setEndValue(scale)
|
||||
.setStartValue(SCALE_PROPERTY.get(v))
|
||||
.setStartVelocity(velocityPxPerS)
|
||||
.build(v, SCALE_PROPERTY);
|
||||
|
||||
}
|
||||
}
|
|
@ -159,7 +159,9 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
|||
StateAnimationConfig config, PendingAnimation builder) {
|
||||
float targetProgress = toState.getVerticalProgress(mLauncher);
|
||||
if (Float.compare(mProgress, targetProgress) == 0) {
|
||||
setAlphas(toState, config, builder);
|
||||
if (!config.onlyPlayAtomicComponent()) {
|
||||
setAlphas(toState, config, builder);
|
||||
}
|
||||
// Fail fast
|
||||
onProgressAnimationEnd();
|
||||
return;
|
||||
|
|
|
@ -126,7 +126,7 @@ public final class FeatureFlags {
|
|||
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
|
||||
"ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
|
||||
|
||||
public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
|
||||
public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
|
||||
"HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
|
||||
|
||||
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
|
||||
|
|
|
@ -26,10 +26,6 @@ import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTI
|
|||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
|
||||
import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -37,7 +33,6 @@ import android.animation.AnimatorSet;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
|
@ -81,7 +76,10 @@ import com.android.launcher3.dragndrop.DragController;
|
|||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo.FolderListener;
|
||||
|
@ -99,7 +97,9 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a set of icons chosen by the user or generated by the system.
|
||||
|
@ -109,6 +109,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
|
||||
private static final String TAG = "Launcher.Folder";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Used for separating folder title when logging together.
|
||||
*/
|
||||
private static final CharSequence FOLDER_LABEL_DELIMITER = "~";
|
||||
|
||||
/**
|
||||
* We avoid measuring {@link #mContent} with a 0 width or height, as this
|
||||
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
|
||||
|
@ -155,6 +161,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
protected final Launcher mLauncher;
|
||||
protected DragController mDragController;
|
||||
public FolderInfo mInfo;
|
||||
private CharSequence mFromTitle;
|
||||
private FromState mFromLabelState;
|
||||
|
||||
@Thunk FolderIcon mFolderIcon;
|
||||
|
||||
|
@ -315,11 +323,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
public void startEditingFolderName() {
|
||||
post(() -> {
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
ofNullable(mInfo)
|
||||
.map(info -> info.suggestedFolderNames)
|
||||
.map(folderNames -> (FolderNameInfo[]) folderNames
|
||||
.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
|
||||
.ifPresent(this::showLabelSuggestions);
|
||||
showLabelSuggestions();
|
||||
}
|
||||
mFolderName.setHint("");
|
||||
mIsEditingName = true;
|
||||
|
@ -334,12 +338,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "onBackKey newTitle=" + newTitle);
|
||||
}
|
||||
mInfo.setTitle(newTitle);
|
||||
mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
|
||||
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
|
||||
mLauncher.getModelWriter());
|
||||
mInfo.setTitle(newTitle, mLauncher.getModelWriter());
|
||||
mFolderIcon.onTitleChanged(newTitle);
|
||||
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
|
||||
|
||||
if (TextUtils.isEmpty(mInfo.title)) {
|
||||
mFolderName.setHint(R.string.folder_hint_text);
|
||||
|
@ -415,6 +415,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
|
||||
void bind(FolderInfo info) {
|
||||
mInfo = info;
|
||||
mFromTitle = info.title;
|
||||
mFromLabelState = info.getFromLabelState();
|
||||
ArrayList<WorkspaceItemInfo> children = info.contents;
|
||||
Collections.sort(children, ITEM_POS_COMPARATOR);
|
||||
updateItemLocationsInDatabaseBatch(true);
|
||||
|
@ -448,32 +450,24 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
|
||||
* rest of the suggestions to InputMethodManager.
|
||||
*/
|
||||
private void showLabelSuggestions(FolderNameInfo[] nameInfos) {
|
||||
if (nameInfos == null) {
|
||||
private void showLabelSuggestions() {
|
||||
if (mInfo.suggestedFolderNames == null) {
|
||||
return;
|
||||
}
|
||||
// Open the Folder and Keyboard when the first or second suggestion is valid non-empty
|
||||
// string.
|
||||
boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
|
||||
nameInfos[0].getLabel())
|
||||
|| nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
|
||||
nameInfos[1].getLabel());
|
||||
|
||||
if (shouldOpen) {
|
||||
if (mInfo.suggestedFolderNames.hasSuggestions()) {
|
||||
// update the primary suggestion if the folder name is empty.
|
||||
if (isEmpty(mFolderName.getText())) {
|
||||
CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
|
||||
if (!isEmpty(firstLabel)) {
|
||||
if (mInfo.suggestedFolderNames.hasPrimary()) {
|
||||
mFolderName.setHint("");
|
||||
mFolderName.setText(firstLabel);
|
||||
mFolderName.setText(mInfo.suggestedFolderNames.getLabels()[0]);
|
||||
mFolderName.selectAll();
|
||||
}
|
||||
}
|
||||
mFolderName.showKeyboard();
|
||||
mFolderName.displayCompletions(
|
||||
asList(nameInfos).subList(0, nameInfos.length).stream()
|
||||
Stream.of(mInfo.suggestedFolderNames.getLabels())
|
||||
.filter(Objects::nonNull)
|
||||
.map(s -> s.getLabel().toString())
|
||||
.map(Object::toString)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.filter(s -> !s.equalsIgnoreCase(mFolderName.getText().toString()))
|
||||
.collect(Collectors.toList()));
|
||||
|
@ -1005,16 +999,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (!items.isEmpty()) {
|
||||
mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
|
||||
}
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind) {
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind
|
||||
&& total > 1 /* no need to update if there's one icon */) {
|
||||
Executors.MODEL_EXECUTOR.post(() -> {
|
||||
FolderNameInfo[] nameInfos =
|
||||
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
|
||||
FolderNameInfos nameInfos = new FolderNameInfos();
|
||||
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
|
||||
fnp.getSuggestedFolderName(
|
||||
getContext(), mInfo.contents, nameInfos);
|
||||
mInfo.suggestedFolderNames = new Intent().putExtra(
|
||||
FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
|
||||
nameInfos);
|
||||
mInfo.suggestedFolderNames = nameInfos;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1337,8 +1329,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (d.stateAnnouncer != null) {
|
||||
d.stateAnnouncer.completeAction(R.string.item_moved);
|
||||
}
|
||||
mStatsLogManager
|
||||
.log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo);
|
||||
mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
|
||||
.log(LAUNCHER_ITEM_DROP_COMPLETED);
|
||||
}
|
||||
|
||||
// This is used so the item doesn't immediately appear in the folder when added. In one case
|
||||
|
@ -1441,10 +1433,38 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (v == mFolderName) {
|
||||
if (hasFocus) {
|
||||
mFromLabelState = mInfo.getFromLabelState();
|
||||
mFromTitle = mInfo.title;
|
||||
startEditingFolderName();
|
||||
} else {
|
||||
mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo);
|
||||
logFolderLabelState();
|
||||
StatsLogger statsLogger = mStatsLogManager.logger()
|
||||
.withItemInfo(mInfo)
|
||||
.withFromState(mFromLabelState);
|
||||
|
||||
// If the folder label is suggested, it is logged to improve prediction model.
|
||||
// When both old and new labels are logged together delimiter is used.
|
||||
StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
|
||||
if (mFromLabelState.equals(FromState.FROM_SUGGESTED)) {
|
||||
labelInfoBuilder.add(mFromTitle);
|
||||
}
|
||||
|
||||
ToState toLabelState;
|
||||
if (mFromTitle != null && mFromTitle.equals(mInfo.title)) {
|
||||
toLabelState = ToState.UNCHANGED;
|
||||
} else {
|
||||
toLabelState = mInfo.getToLabelState();
|
||||
if (toLabelState.toString().startsWith("TO_SUGGESTION")) {
|
||||
labelInfoBuilder.add(mInfo.title);
|
||||
}
|
||||
}
|
||||
statsLogger.withToState(toLabelState);
|
||||
|
||||
if (labelInfoBuilder.length() > 0) {
|
||||
statsLogger.withEditText(labelInfoBuilder.toString());
|
||||
}
|
||||
|
||||
statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
|
||||
logFolderLabelState(mFromLabelState, toLabelState);
|
||||
mFolderName.dispatchBackKey();
|
||||
}
|
||||
}
|
||||
|
@ -1649,8 +1669,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
* @deprecated This method is only used for log validation and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
public void logFolderLabelState() {
|
||||
public void logFolderLabelState(FromState fromState, ToState toState) {
|
||||
mLauncher.getUserEventDispatcher()
|
||||
.logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
|
||||
.logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
package com.android.launcher3.folder;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -63,11 +63,14 @@ import com.android.launcher3.dragndrop.DragLayer;
|
|||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo.FolderListener;
|
||||
import com.android.launcher3.model.data.FolderInfo.LabelState;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
|
@ -81,6 +84,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
||||
/**
|
||||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||
*/
|
||||
|
@ -406,10 +410,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
|
||||
final int finalIndex = index;
|
||||
|
||||
FolderNameInfo[] nameInfos =
|
||||
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
|
||||
FolderNameInfos nameInfos = new FolderNameInfos();
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
Executors.UI_HELPER_EXECUTOR.post(() -> {
|
||||
Executors.MODEL_EXECUTOR.post(() -> {
|
||||
d.folderNameProvider.getSuggestedFolderName(
|
||||
getContext(), mInfo.contents, nameInfos);
|
||||
showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
|
||||
|
@ -423,12 +426,11 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
}
|
||||
|
||||
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
|
||||
FolderNameInfo[] nameInfos, InstanceId instanceId) {
|
||||
FolderNameInfos nameInfos, InstanceId instanceId) {
|
||||
postDelayed(() -> {
|
||||
mPreviewItemManager.hidePreviewItem(finalIndex, false);
|
||||
mFolder.showItem(item);
|
||||
setLabelSuggestion(nameInfos, instanceId);
|
||||
mFolder.logFolderLabelState();
|
||||
invalidate();
|
||||
}, DROP_IN_ANIMATION_DURATION);
|
||||
}
|
||||
|
@ -436,23 +438,45 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
/**
|
||||
* Set the suggested folder name.
|
||||
*/
|
||||
public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
|
||||
public void setLabelSuggestion(FolderNameInfos nameInfos, InstanceId instanceId) {
|
||||
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
return;
|
||||
}
|
||||
if (!isEmpty(mFolderName.getText().toString())
|
||||
|| mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
|
||||
if (!mInfo.getLabelState().equals(LabelState.UNLABELED)) {
|
||||
return;
|
||||
}
|
||||
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
|
||||
if (nameInfos == null || !nameInfos.hasSuggestions()) {
|
||||
StatsLogManager.newInstance(getContext()).logger()
|
||||
.withInstanceId(instanceId)
|
||||
.withItemInfo(mInfo)
|
||||
.log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS);
|
||||
return;
|
||||
}
|
||||
mInfo.setTitle(nameInfos[0].getLabel());
|
||||
StatsLogManager.newInstance(getContext())
|
||||
.log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo);
|
||||
if (!nameInfos.hasPrimary()) {
|
||||
StatsLogManager.newInstance(getContext()).logger()
|
||||
.withInstanceId(instanceId)
|
||||
.withItemInfo(mInfo)
|
||||
.log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY);
|
||||
return;
|
||||
}
|
||||
CharSequence newTitle = nameInfos.getLabels()[0];
|
||||
FromState fromState = mInfo.getFromLabelState();
|
||||
|
||||
mInfo.setTitle(newTitle, mFolder.mLauncher.getModelWriter());
|
||||
onTitleChanged(mInfo.title);
|
||||
mFolder.mFolderName.setText(mInfo.title);
|
||||
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
|
||||
|
||||
// Logging for folder creation flow
|
||||
StatsLogManager.newInstance(getContext()).logger()
|
||||
.withInstanceId(instanceId)
|
||||
.withItemInfo(mInfo)
|
||||
.withFromState(fromState)
|
||||
.withToState(ToState.TO_SUGGESTION0)
|
||||
// When LAUNCHER_FOLDER_LABEL_UPDATED event.edit_text does not have delimiter,
|
||||
// event is assumed to be folder creation on the server side.
|
||||
.withEditText(newTitle.toString())
|
||||
.log(LAUNCHER_FOLDER_AUTO_LABELED);
|
||||
mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* 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.launcher3.folder;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Information about a single label suggestions of the Folder.
|
||||
*/
|
||||
|
||||
public final class FolderNameInfo implements Parcelable {
|
||||
private final double mScore;
|
||||
private final CharSequence mLabel;
|
||||
|
||||
/**
|
||||
* Create a simple completion with label.
|
||||
*
|
||||
* @param label The text that should be inserted into the editor and pushed to
|
||||
* InputMethodManager suggestions.
|
||||
* @param score The score for the label between 0.0 and 1.0.
|
||||
*/
|
||||
public FolderNameInfo(CharSequence label, double score) {
|
||||
mScore = score;
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
private FolderNameInfo(Parcel source) {
|
||||
mScore = source.readDouble();
|
||||
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
|
||||
}
|
||||
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return mScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to package this object into a {@link Parcel}.
|
||||
*
|
||||
* @param dest The {@link Parcel} to be written.
|
||||
* @param flags The flags used for parceling.
|
||||
*/
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeDouble(mScore);
|
||||
TextUtils.writeToParcel(mLabel, dest, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*/
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<FolderNameInfo> CREATOR =
|
||||
new Parcelable.Creator<FolderNameInfo>() {
|
||||
public FolderNameInfo createFromParcel(Parcel source) {
|
||||
return new FolderNameInfo(source);
|
||||
}
|
||||
|
||||
public FolderNameInfo[] newArray(int size) {
|
||||
return new FolderNameInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return String.format("%s:%.2f", mLabel, mScore);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.launcher3.folder;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Information about a label suggestions of a Folder.
|
||||
*/
|
||||
|
||||
public class FolderNameInfos {
|
||||
public static final int SUCCESS = 1;
|
||||
public static final int HAS_PRIMARY = 1 << 1;
|
||||
public static final int HAS_SUGGESTIONS = 1 << 2;
|
||||
public static final int ERROR_NO_PROVIDER = 1 << 3;
|
||||
public static final int ERROR_APP_LOOKUP_FAILED = 1 << 4;
|
||||
public static final int ERROR_ALL_APP_LOOKUP_FAILED = 1 << 5;
|
||||
public static final int ERROR_NO_LABELS_GENERATED = 1 << 6;
|
||||
public static final int ERROR_LABEL_LOOKUP_FAILED = 1 << 7;
|
||||
public static final int ERROR_ALL_LABEL_LOOKUP_FAILED = 1 << 8;
|
||||
public static final int ERROR_NO_PACKAGES = 1 << 9;
|
||||
|
||||
private int mStatus;
|
||||
private final CharSequence[] mLabels;
|
||||
private final Float[] mScores;
|
||||
|
||||
public FolderNameInfos() {
|
||||
mStatus = 0;
|
||||
mLabels = new CharSequence[FolderNameProvider.SUGGEST_MAX];
|
||||
mScores = new Float[FolderNameProvider.SUGGEST_MAX];
|
||||
}
|
||||
|
||||
/**
|
||||
* set the status of FolderNameInfos.
|
||||
*/
|
||||
public void setStatus(int statusBit) {
|
||||
mStatus = mStatus | statusBit;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns status of FolderNameInfos generations.
|
||||
*/
|
||||
public int status() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if the first suggestion is a Primary suggestion.
|
||||
*/
|
||||
public boolean hasPrimary() {
|
||||
return (mStatus & HAS_PRIMARY) > 0 && (mLabels[0] != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if there is at least one valid suggestion.
|
||||
*/
|
||||
public boolean hasSuggestions() {
|
||||
for (CharSequence l : mLabels) {
|
||||
if (l != null && !TextUtils.isEmpty(l)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* assign label and score in the specified index.
|
||||
*/
|
||||
public void setLabel(int index, CharSequence label, Float score) {
|
||||
if (index < mLabels.length) {
|
||||
mLabels[index] = label;
|
||||
mScores[index] = score;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the label is found in label suggestions/
|
||||
*/
|
||||
public boolean contains(CharSequence label) {
|
||||
return Arrays.stream(mLabels)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(l -> l.toString().equalsIgnoreCase(label.toString()));
|
||||
}
|
||||
|
||||
|
||||
public CharSequence[] getLabels() {
|
||||
return mLabels;
|
||||
}
|
||||
|
||||
public Float[] getScores() {
|
||||
return mScores;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return String.format("status=%s, labels=%s", Integer.toBinaryString(mStatus),
|
||||
Arrays.toString(mLabels));
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ import com.android.launcher3.model.data.AppInfo;
|
|||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -64,6 +65,7 @@ public class FolderNameProvider implements ResourceBasedOverride {
|
|||
public static FolderNameProvider newInstance(Context context) {
|
||||
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
|
||||
context.getApplicationContext(), R.string.folder_name_provider_class);
|
||||
Preconditions.assertWorkerThread();
|
||||
fnp.load(context);
|
||||
|
||||
return fnp;
|
||||
|
@ -71,6 +73,7 @@ public class FolderNameProvider implements ResourceBasedOverride {
|
|||
|
||||
public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
|
||||
IntSparseArrayMap<FolderInfo> folderInfos) {
|
||||
Preconditions.assertWorkerThread();
|
||||
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
|
||||
context.getApplicationContext(), R.string.folder_name_provider_class);
|
||||
fnp.load(appInfos, folderInfos);
|
||||
|
@ -93,10 +96,10 @@ public class FolderNameProvider implements ResourceBasedOverride {
|
|||
*/
|
||||
public void getSuggestedFolderName(Context context,
|
||||
ArrayList<WorkspaceItemInfo> workspaceItemInfos,
|
||||
FolderNameInfo[] nameInfos) {
|
||||
|
||||
FolderNameInfos nameInfos) {
|
||||
Preconditions.assertWorkerThread();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
|
||||
Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
|
||||
}
|
||||
// If all the icons are from work profile,
|
||||
// Then, suggest "Work" as the folder name
|
||||
|
@ -121,7 +124,7 @@ public class FolderNameProvider implements ResourceBasedOverride {
|
|||
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
|
||||
Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,39 +138,37 @@ public class FolderNameProvider implements ResourceBasedOverride {
|
|||
.findAny();
|
||||
}
|
||||
|
||||
private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
|
||||
if (nameInfos.length == 0 || contains(nameInfos, label)) {
|
||||
private void setAsFirstSuggestion(FolderNameInfos nameInfos, CharSequence label) {
|
||||
if (nameInfos == null || nameInfos.contains(label)) {
|
||||
return;
|
||||
}
|
||||
for (int i = nameInfos.length - 1; i > 0; i--) {
|
||||
if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
|
||||
nameInfos[i] = nameInfos[i - 1];
|
||||
nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
|
||||
nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
|
||||
CharSequence[] labels = nameInfos.getLabels();
|
||||
Float[] scores = nameInfos.getScores();
|
||||
for (int i = labels.length - 1; i > 0; i--) {
|
||||
if (labels[i - 1] != null && !TextUtils.isEmpty(labels[i - 1])) {
|
||||
nameInfos.setLabel(i, labels[i - 1], scores[i - 1]);
|
||||
}
|
||||
}
|
||||
nameInfos[0] = new FolderNameInfo(label, 1.0);
|
||||
nameInfos.setLabel(0, label, 1.0f);
|
||||
}
|
||||
|
||||
private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
|
||||
if (nameInfos.length == 0 || contains(nameInfos, label)) {
|
||||
private void setAsLastSuggestion(FolderNameInfos nameInfos, CharSequence label) {
|
||||
if (nameInfos == null || nameInfos.contains(label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nameInfos.length; i++) {
|
||||
if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
|
||||
nameInfos[i] = new FolderNameInfo(label, 1.0);
|
||||
nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
|
||||
nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
|
||||
CharSequence[] labels = nameInfos.getLabels();
|
||||
for (int i = 0; i < labels.length; i++) {
|
||||
if (labels[i] == null || TextUtils.isEmpty(labels[i])) {
|
||||
nameInfos.setLabel(i, label, 1.0f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Overwrite the last suggestion.
|
||||
int lastIndex = nameInfos.length - 1;
|
||||
nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
|
||||
}
|
||||
|
||||
private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
|
||||
return Arrays.stream(nameInfos)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
|
||||
label.toString()));
|
||||
nameInfos.setLabel(labels.length - 1, label, 1.0f);
|
||||
}
|
||||
|
||||
private class FolderNameWorker extends BaseModelUpdateTask {
|
||||
|
|
|
@ -15,15 +15,22 @@
|
|||
*/
|
||||
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_OPEN_UP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
|
||||
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.userevent.LauncherLogProto;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
/**
|
||||
|
@ -34,6 +41,54 @@ import com.android.launcher3.util.ResourceBasedOverride;
|
|||
*/
|
||||
public class StatsLogManager implements ResourceBasedOverride {
|
||||
|
||||
public static final int LAUNCHER_STATE_UNSPECIFIED = 0;
|
||||
public static final int LAUNCHER_STATE_BACKGROUND = 1;
|
||||
public static final int LAUNCHER_STATE_HOME = 2;
|
||||
public static final int LAUNCHER_STATE_OVERVIEW = 3;
|
||||
public static final int LAUNCHER_STATE_ALLAPPS = 4;
|
||||
public static final int LAUNCHER_STATE_UNCHANGED = 5;
|
||||
|
||||
/**
|
||||
* Returns proper launcher state enum for {@link StatsLogManager}
|
||||
* (to be removed during UserEventDispatcher cleanup)
|
||||
*/
|
||||
public static int containerTypeToAtomState(int containerType) {
|
||||
switch (containerType) {
|
||||
case LauncherLogProto.ContainerType.ALLAPPS_VALUE:
|
||||
return LAUNCHER_STATE_ALLAPPS;
|
||||
case LauncherLogProto.ContainerType.OVERVIEW_VALUE:
|
||||
return LAUNCHER_STATE_OVERVIEW;
|
||||
case LauncherLogProto.ContainerType.WORKSPACE_VALUE:
|
||||
return LAUNCHER_STATE_HOME;
|
||||
case LauncherLogProto.ContainerType.APP_VALUE:
|
||||
return LAUNCHER_STATE_BACKGROUND;
|
||||
}
|
||||
return LAUNCHER_STATE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event enum based on the two {@link ContainerType} transition information when
|
||||
* swipe gesture happens.
|
||||
* (to be removed during UserEventDispatcher cleanup)
|
||||
*/
|
||||
public static EventEnum getLauncherAtomEvent(int startContainerType,
|
||||
int targetContainerType, EventEnum fallbackEvent) {
|
||||
if (startContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()
|
||||
&& targetContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()) {
|
||||
return LAUNCHER_HOME_GESTURE;
|
||||
} else if (startContainerType != LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()
|
||||
&& targetContainerType == LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()) {
|
||||
return LAUNCHER_OVERVIEW_GESTURE;
|
||||
} else if (startContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()
|
||||
&& targetContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
|
||||
return LAUNCHER_ALLAPPS_OPEN_UP;
|
||||
} else if (startContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()
|
||||
&& targetContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
|
||||
return LAUNCHER_ALLAPPS_CLOSE_DOWN;
|
||||
}
|
||||
return fallbackEvent; // TODO fix
|
||||
}
|
||||
|
||||
public interface EventEnum {
|
||||
int getId();
|
||||
}
|
||||
|
@ -67,8 +122,16 @@ public class StatsLogManager implements ResourceBasedOverride {
|
|||
+ "resulting in a new folder creation")
|
||||
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
|
||||
|
||||
@UiEvent(doc = "User action resulted in or manually updated the folder label to "
|
||||
+ "new/same value.")
|
||||
@UiEvent(doc = "Folder's label is automatically assigned.")
|
||||
LAUNCHER_FOLDER_AUTO_LABELED(591),
|
||||
|
||||
@UiEvent(doc = "Could not auto-label a folder because primary suggestion is null or empty.")
|
||||
LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY(592),
|
||||
|
||||
@UiEvent(doc = "Could not auto-label a folder because no suggestions exist.")
|
||||
LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS(593),
|
||||
|
||||
@UiEvent(doc = "User manually updated the folder label.")
|
||||
LAUNCHER_FOLDER_LABEL_UPDATED(460),
|
||||
|
||||
@UiEvent(doc = "User long pressed on the workspace empty space.")
|
||||
|
@ -145,12 +208,75 @@ public class StatsLogManager implements ResourceBasedOverride {
|
|||
@UiEvent(doc = "Hotseat education tip shown")
|
||||
LAUNCHER_HOTSEAT_EDU_ONLY_TIP(482),
|
||||
|
||||
/**
|
||||
* @deprecated LauncherUiChanged.rank field is repurposed to store all apps rank, so no
|
||||
* separate event is required.
|
||||
*/
|
||||
@Deprecated
|
||||
@UiEvent(doc = "App launch ranking logged for all apps predictions")
|
||||
LAUNCHER_ALL_APPS_RANKED(552),
|
||||
|
||||
@UiEvent(doc = "App launch ranking logged for hotseat predictions)")
|
||||
LAUNCHER_HOTSEAT_RANKED(553);
|
||||
LAUNCHER_HOTSEAT_RANKED(553),
|
||||
@UiEvent(doc = "Launcher is now in background. e.g., Screen off event")
|
||||
LAUNCHER_ONSTOP(562),
|
||||
|
||||
@UiEvent(doc = "Launcher is now in foreground. e.g., Screen on event, back button")
|
||||
LAUNCHER_ONRESUME(563),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in LEFT direction on workspace.")
|
||||
LAUNCHER_SWIPELEFT(564),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in RIGHT direction on workspace.")
|
||||
LAUNCHER_SWIPERIGHT(565),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in UP direction in unknown way.")
|
||||
LAUNCHER_UNKNOWN_SWIPEUP(566),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in DOWN direction in unknown way.")
|
||||
LAUNCHER_UNKNOWN_SWIPEDOWN(567),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in UP direction to open apps drawer.")
|
||||
LAUNCHER_ALLAPPS_OPEN_UP(568),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in DOWN direction to close apps drawer.")
|
||||
LAUNCHER_ALLAPPS_CLOSE_DOWN(569),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in UP direction and hold from the bottom bazel area")
|
||||
LAUNCHER_OVERVIEW_GESTURE(570),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in LEFT direction on the bottom bazel area.")
|
||||
LAUNCHER_QUICKSWITCH_LEFT(571),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in RIGHT direction on the bottom bazel area.")
|
||||
LAUNCHER_QUICKSWITCH_RIGHT(572),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in DOWN direction on the bottom bazel area.")
|
||||
LAUNCHER_SWIPEDOWN_NAVBAR(573),
|
||||
|
||||
@UiEvent(doc = "User swipes or fling in UP direction from bottom bazel area.")
|
||||
LAUNCHER_HOME_GESTURE(574),
|
||||
|
||||
@UiEvent(doc = "User's workspace layout information is snapshot in the background.")
|
||||
LAUNCHER_WORKSPACE_SNAPSHOT(579),
|
||||
|
||||
@UiEvent(doc = "User tapped on the screenshot button on overview)")
|
||||
LAUNCHER_OVERVIEW_ACTIONS_SCREENSHOT(580),
|
||||
|
||||
@UiEvent(doc = "User tapped on the select button on overview)")
|
||||
LAUNCHER_OVERVIEW_ACTIONS_SELECT(581),
|
||||
|
||||
@UiEvent(doc = "User tapped on the share button on overview")
|
||||
LAUNCHER_OVERVIEW_ACTIONS_SHARE(582),
|
||||
|
||||
@UiEvent(doc = "User tapped on the close button in select mode")
|
||||
LAUNCHER_SELECT_MODE_CLOSE(583),
|
||||
|
||||
@UiEvent(doc = "User tapped on the highlight items in select mode")
|
||||
LAUNCHER_SELECT_MODE_ITEM(584);
|
||||
|
||||
// ADD MORE
|
||||
|
||||
private final int mId;
|
||||
|
||||
LauncherEvent(int id) {
|
||||
|
@ -181,77 +307,102 @@ public class StatsLogManager implements ResourceBasedOverride {
|
|||
}
|
||||
}
|
||||
|
||||
protected LogStateProvider mStateProvider;
|
||||
/**
|
||||
* Helps to construct and write the log message.
|
||||
*/
|
||||
public interface StatsLogger {
|
||||
|
||||
/**
|
||||
* Sets log fields from provided {@link ItemInfo}.
|
||||
*/
|
||||
default StatsLogger withItemInfo(ItemInfo itemInfo) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets {@link InstanceId} of log message.
|
||||
*/
|
||||
default StatsLogger withInstanceId(InstanceId instanceId) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rank field of log message.
|
||||
*/
|
||||
default StatsLogger withRank(int rank) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets source launcher state field of log message.
|
||||
*/
|
||||
default StatsLogger withSrcState(int srcState) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets destination launcher state field of log message.
|
||||
*/
|
||||
default StatsLogger withDstState(int dstState) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets FromState field of log message.
|
||||
*/
|
||||
default StatsLogger withFromState(FromState fromState) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ToState field of log message.
|
||||
*/
|
||||
default StatsLogger withToState(ToState toState) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets editText field of log message.
|
||||
*/
|
||||
default StatsLogger withEditText(String editText) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the final value for container related fields of log message.
|
||||
*
|
||||
* By default container related fields are derived from {@link ItemInfo}, this method would
|
||||
* override those values.
|
||||
*/
|
||||
default StatsLogger withContainerInfo(ContainerInfo containerInfo) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the final message and logs it as {@link EventEnum}.
|
||||
*/
|
||||
default void log(EventEnum event) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new logger object.
|
||||
*/
|
||||
public StatsLogger logger() {
|
||||
return new StatsLogger() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link StatsLogManager} based on provided context.
|
||||
*/
|
||||
public static StatsLogManager newInstance(Context context) {
|
||||
return newInstance(context, null);
|
||||
}
|
||||
|
||||
public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
|
||||
StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
|
||||
context.getApplicationContext(), R.string.stats_log_manager_class);
|
||||
mgr.mStateProvider = stateProvider;
|
||||
return mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
*/
|
||||
public void log(EventEnum event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param instanceId an identifier obtained from an InstanceIdSequence.
|
||||
*/
|
||||
public void log(EventEnum event, InstanceId instanceId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param itemInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param atomInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link LauncherState}s.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param launcherAtomItemInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo launcherAtomItemInfo,
|
||||
int srcState, int dstState) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event.
|
||||
*
|
||||
* @param event an enum implementing EventEnum interface.
|
||||
* @param instanceId an identifier obtained from an InstanceIdSequence.
|
||||
* @param itemInfo item typically containing app or task launch related information.
|
||||
*/
|
||||
public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an event with ranked-choice information along with package. Does nothing if event.getId()
|
||||
* <= 0.
|
||||
|
@ -265,13 +416,6 @@ public class StatsLogManager implements ResourceBasedOverride {
|
|||
int position) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link LauncherState}s. If either of the state refers
|
||||
* to workspace state, then use pageIndex to pass in index of workspace.
|
||||
*/
|
||||
public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs snapshot, or impression of the current workspace.
|
||||
*/
|
||||
|
|
|
@ -6,28 +6,13 @@ import android.view.ViewParent;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
|
||||
|
||||
public class StatsLogUtils {
|
||||
|
||||
// Defined in android.stats.launcher.nano
|
||||
// As they cannot be linked in this file, defining again.
|
||||
public final static int LAUNCHER_STATE_BACKGROUND = 0;
|
||||
public final static int LAUNCHER_STATE_HOME = 1;
|
||||
public final static int LAUNCHER_STATE_OVERVIEW = 2;
|
||||
public final static int LAUNCHER_STATE_ALLAPPS = 3;
|
||||
|
||||
private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
|
||||
|
||||
public interface LogStateProvider {
|
||||
int getCurrentState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented by containers to provide a container source for a given child.
|
||||
*/
|
||||
|
@ -61,20 +46,4 @@ public class StatsLogUtils {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getContainerTypeFromState(int state) {
|
||||
int containerType = DEFAULT_CONTAINERTYPE;
|
||||
switch (state) {
|
||||
case StatsLogUtils.LAUNCHER_STATE_ALLAPPS:
|
||||
containerType = ContainerType.ALLAPPS;
|
||||
break;
|
||||
case StatsLogUtils.LAUNCHER_STATE_HOME:
|
||||
containerType = ContainerType.WORKSPACE;
|
||||
break;
|
||||
case StatsLogUtils.LAUNCHER_STATE_OVERVIEW:
|
||||
containerType = ContainerType.OVERVIEW;
|
||||
break;
|
||||
}
|
||||
return containerType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class AppLaunchTracker implements ResourceBasedOverride {
|
|||
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 =
|
||||
|
|
|
@ -59,7 +59,7 @@ import com.android.launcher3.Utilities;
|
|||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderGridOrganizer;
|
||||
import com.android.launcher3.folder.FolderNameInfo;
|
||||
import com.android.launcher3.folder.FolderNameInfos;
|
||||
import com.android.launcher3.folder.FolderNameProvider;
|
||||
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
|
||||
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
|
||||
|
@ -953,13 +953,12 @@ public class LoaderTask implements Runnable {
|
|||
|
||||
synchronized (mBgDataModel) {
|
||||
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
|
||||
FolderNameInfo[] suggestionInfos =
|
||||
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
|
||||
FolderNameInfos suggestionInfos = new FolderNameInfos();
|
||||
FolderInfo info = mBgDataModel.folders.valueAt(i);
|
||||
if (info.suggestedFolderNames == null) {
|
||||
provider.getSuggestedFolderName(mApp.getContext(), info.contents,
|
||||
suggestionInfos);
|
||||
info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
|
||||
info.suggestedFolderNames = suggestionInfos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import static androidx.core.util.Preconditions.checkNotNull;
|
|||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL;
|
||||
import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
|
||||
import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
|
||||
|
@ -29,18 +30,16 @@ import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolder
|
|||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.FolderNameInfo;
|
||||
import com.android.launcher3.folder.FolderNameInfos;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.Attribute;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
|
@ -51,10 +50,7 @@ import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelStat
|
|||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
||||
|
@ -82,25 +78,35 @@ public class FolderInfo extends ItemInfo {
|
|||
|
||||
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
|
||||
|
||||
/**
|
||||
* Different states of folder label.
|
||||
*/
|
||||
public enum LabelState {
|
||||
// Folder's label is not yet assigned( i.e., title == null). Eligible for auto-labeling.
|
||||
UNLABELED(Attribute.UNLABELED),
|
||||
|
||||
// Folder's label is empty(i.e., title == ""). Not eligible for auto-labeling.
|
||||
EMPTY(EMPTY_LABEL),
|
||||
|
||||
// Folder's label is one of the non-empty suggested values.
|
||||
SUGGESTED(SUGGESTED_LABEL),
|
||||
|
||||
// Folder's label is non-empty, manually entered by the user
|
||||
// and different from any of suggested values.
|
||||
MANUAL(MANUAL_LABEL);
|
||||
|
||||
private final LauncherAtom.Attribute mLogAttribute;
|
||||
|
||||
LabelState(Attribute logAttribute) {
|
||||
this.mLogAttribute = logAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
|
||||
|
||||
public int options;
|
||||
|
||||
public Intent suggestedFolderNames;
|
||||
|
||||
// Represents the title before current.
|
||||
// Primarily used for logging purpose.
|
||||
private CharSequence mPreviousTitle;
|
||||
|
||||
// True if the title before was manually entered, suggested otherwise.
|
||||
// Primarily used for logging purpose.
|
||||
public boolean fromCustom;
|
||||
|
||||
/**
|
||||
* Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
|
||||
* for logging.
|
||||
*/
|
||||
private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
|
||||
public FolderNameInfos suggestedFolderNames;
|
||||
|
||||
/**
|
||||
* The apps and shortcuts
|
||||
|
@ -198,8 +204,7 @@ public class FolderInfo extends ItemInfo {
|
|||
|
||||
@Override
|
||||
protected String dumpProperties() {
|
||||
return super.dumpProperties()
|
||||
+ " manuallyTypedTitle=" + hasOption(FLAG_MANUAL_FOLDER_NAME);
|
||||
return String.format("%s; labelState=%s", super.dumpProperties(), getLabelState());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -207,19 +212,41 @@ public class FolderInfo extends ItemInfo {
|
|||
return getDefaultItemInfoBuilder()
|
||||
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
|
||||
.setRank(rank)
|
||||
.setAttribute(fromCustom ? MANUAL_LABEL : SUGGESTED_LABEL)
|
||||
.setAttribute(getLabelState().mLogAttribute)
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mPreviousTitle = this.title;
|
||||
public void setTitle(@Nullable CharSequence title, ModelWriter modelWriter) {
|
||||
// Updating label from null to empty is considered as false touch.
|
||||
// Retaining null title(ie., UNLABELED state) allows auto-labeling when new items added.
|
||||
if (isEmpty(title) && this.title == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Updating title to same value does not change any states.
|
||||
if (title != null && title == this.title) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
LabelState newLabelState =
|
||||
title == null ? LabelState.UNLABELED
|
||||
: title.length() == 0 ? LabelState.EMPTY :
|
||||
getAcceptedSuggestionIndex().isPresent() ? LabelState.SUGGESTED
|
||||
: LabelState.MANUAL;
|
||||
setOption(FLAG_MANUAL_FOLDER_NAME, newLabelState.equals(LabelState.MANUAL), modelWriter);
|
||||
}
|
||||
|
||||
public CharSequence getPreviousTitle() {
|
||||
return mPreviousTitle;
|
||||
/**
|
||||
* Returns current state of the current folder label.
|
||||
*/
|
||||
public LabelState getLabelState() {
|
||||
return title == null ? LabelState.UNLABELED
|
||||
: title.length() == 0 ? LabelState.EMPTY :
|
||||
hasOption(FLAG_MANUAL_FOLDER_NAME) ? LabelState.MANUAL
|
||||
: LabelState.SUGGESTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -235,30 +262,7 @@ public class FolderInfo extends ItemInfo {
|
|||
*/
|
||||
@Override
|
||||
public LauncherAtom.ItemInfo buildProto() {
|
||||
FromState fromFolderLabelState = getFromFolderLabelState();
|
||||
ToState toFolderLabelState = getToFolderLabelState();
|
||||
LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
|
||||
.setCardinality(contents.size())
|
||||
.setFromLabelState(fromFolderLabelState)
|
||||
.setToLabelState(toFolderLabelState);
|
||||
|
||||
// If the folder label is suggested, it is logged to improve prediction model.
|
||||
// When both old and new labels are logged together delimiter is used.
|
||||
StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
|
||||
if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
|
||||
labelInfoBuilder.add(mPreviousTitle);
|
||||
}
|
||||
if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
|
||||
labelInfoBuilder.add(title);
|
||||
}
|
||||
if (labelInfoBuilder.length() > 0) {
|
||||
folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
|
||||
}
|
||||
|
||||
return getDefaultItemInfoBuilder()
|
||||
.setFolderIcon(folderIconBuilder)
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build();
|
||||
return buildProto(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,47 +271,58 @@ public class FolderInfo extends ItemInfo {
|
|||
public OptionalInt getAcceptedSuggestionIndex() {
|
||||
String newLabel = checkNotNull(title,
|
||||
"Expected valid folder label, but found null").toString();
|
||||
return getSuggestedLabels()
|
||||
.map(suggestionsArray ->
|
||||
IntStream.range(0, suggestionsArray.length)
|
||||
.filter(
|
||||
index -> !isEmpty(suggestionsArray[index])
|
||||
&& newLabel.equalsIgnoreCase(
|
||||
suggestionsArray[index]))
|
||||
.sequential()
|
||||
.findFirst()
|
||||
).orElse(OptionalInt.empty());
|
||||
|
||||
if (suggestedFolderNames == null || !suggestedFolderNames.hasSuggestions()) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
CharSequence[] labels = suggestedFolderNames.getLabels();
|
||||
return IntStream.range(0, labels.length)
|
||||
.filter(index -> !isEmpty(labels[index])
|
||||
&& newLabel.equalsIgnoreCase(
|
||||
labels[index].toString()))
|
||||
.sequential()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private LauncherAtom.ToState getToFolderLabelState() {
|
||||
/**
|
||||
* Returns {@link FromState} based on current {@link #title}.
|
||||
*/
|
||||
public LauncherAtom.FromState getFromLabelState() {
|
||||
switch (getLabelState()){
|
||||
case EMPTY:
|
||||
return LauncherAtom.FromState.FROM_EMPTY;
|
||||
case MANUAL:
|
||||
return LauncherAtom.FromState.FROM_CUSTOM;
|
||||
case SUGGESTED:
|
||||
return LauncherAtom.FromState.FROM_SUGGESTED;
|
||||
case UNLABELED:
|
||||
default:
|
||||
return LauncherAtom.FromState.FROM_STATE_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link ToState} based on current {@link #title}.
|
||||
*/
|
||||
public LauncherAtom.ToState getToLabelState() {
|
||||
if (title == null) {
|
||||
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
if (title.equals(mPreviousTitle)) {
|
||||
return LauncherAtom.ToState.UNCHANGED;
|
||||
}
|
||||
|
||||
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
return title.length() > 0
|
||||
? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
|
||||
}
|
||||
|
||||
Optional<String[]> suggestedLabels = getSuggestedLabels();
|
||||
boolean isEmptySuggestions = suggestedLabels
|
||||
.map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
|
||||
.orElse(true);
|
||||
if (isEmptySuggestions) {
|
||||
// TODO: if suggestedFolderNames is null then it infrastructure issue, not
|
||||
// ranking issue. We should log these appropriately.
|
||||
if (suggestedFolderNames == null || !suggestedFolderNames.hasSuggestions()) {
|
||||
return title.length() > 0
|
||||
? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
|
||||
}
|
||||
|
||||
boolean hasValidPrimary = suggestedLabels
|
||||
.map(labels -> !isEmpty(labels[0]))
|
||||
.orElse(false);
|
||||
boolean hasValidPrimary = suggestedFolderNames != null && suggestedFolderNames.hasPrimary();
|
||||
if (title.length() == 0) {
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
|
@ -335,31 +350,6 @@ public class FolderInfo extends ItemInfo {
|
|||
// fall through
|
||||
}
|
||||
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
|
||||
|
||||
}
|
||||
|
||||
private LauncherAtom.FromState getFromFolderLabelState() {
|
||||
return mPreviousTitle == null
|
||||
? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
|
||||
: mPreviousTitle.length() == 0
|
||||
? LauncherAtom.FromState.FROM_EMPTY
|
||||
: fromCustom
|
||||
? LauncherAtom.FromState.FROM_CUSTOM
|
||||
: LauncherAtom.FromState.FROM_SUGGESTED;
|
||||
}
|
||||
|
||||
private Optional<String[]> getSuggestedLabels() {
|
||||
return ofNullable(suggestedFolderNames)
|
||||
.map(folderNames ->
|
||||
(FolderNameInfo[])
|
||||
folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
|
||||
.map(folderNameInfoArray ->
|
||||
stream(folderNameInfoArray)
|
||||
.filter(Objects::nonNull)
|
||||
.map(FolderNameInfo::getLabel)
|
||||
.filter(Objects::nonNull)
|
||||
.map(CharSequence::toString)
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,7 +358,8 @@ public class FolderInfo extends ItemInfo {
|
|||
* @deprecated This method is used only for validation purpose and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
|
||||
public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
|
||||
ToState toState) {
|
||||
return LauncherLogProto.LauncherEvent.newBuilder()
|
||||
.setAction(LauncherLogProto.Action
|
||||
.newBuilder()
|
||||
|
@ -377,8 +368,8 @@ public class FolderInfo extends ItemInfo {
|
|||
.newBuilder()
|
||||
.setType(Target.Type.ITEM)
|
||||
.setItemType(LauncherLogProto.ItemType.EDITTEXT)
|
||||
.setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
|
||||
.setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
|
||||
.setFromFolderLabelState(convertFolderLabelState(fromState))
|
||||
.setToFolderLabelState(convertFolderLabelState(toState)))
|
||||
.addSrcTarget(Target.newBuilder()
|
||||
.setType(Target.Type.CONTAINER)
|
||||
.setContainerType(LauncherLogProto.ContainerType.FOLDER)
|
||||
|
|
|
@ -52,6 +52,7 @@ import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
|
|||
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
@ -405,7 +406,10 @@ public class ItemInfo {
|
|||
return itemInfo;
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
/**
|
||||
* Sets the title of the item and writes to DB model if needed.
|
||||
*/
|
||||
public void setTitle(CharSequence title, ModelWriter modelWriter) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,8 @@ public class NotificationInfo implements View.OnClickListener {
|
|||
try {
|
||||
intent.send(null, 0, null, null, null, null, activityOptions);
|
||||
launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
|
||||
launcher.getStatsLogManager()
|
||||
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo);
|
||||
launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
|
||||
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -77,8 +77,8 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
|
|||
@Override
|
||||
public void onClick(View view) {
|
||||
AbstractFloatingView.closeAllOpenViews(mTarget);
|
||||
mTarget.getStatsLogManager()
|
||||
.log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo);
|
||||
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
|
||||
.log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP);
|
||||
|
||||
final String actionIdentity = mAction.getTitle() + ", "
|
||||
+ mItemInfo.getTargetComponent().getPackageName();
|
||||
|
|
|
@ -119,7 +119,8 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
|||
widgetsBottomSheet.populateAndShow(mItemInfo);
|
||||
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
|
||||
ControlType.WIDGETS_BUTTON, view);
|
||||
mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, mItemInfo);
|
||||
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
|
||||
.log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,8 +141,8 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
|||
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
|
||||
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
|
||||
ControlType.APPINFO_TARGET, view);
|
||||
mTarget.getStatsLogManager()
|
||||
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo);
|
||||
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
|
||||
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@ package com.android.launcher3.statemanager;
|
|||
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.LauncherRootView;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
|
||||
import com.android.launcher3.statemanager.StateManager.StateHandler;
|
||||
|
@ -38,6 +41,8 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
|
|||
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
|
||||
private boolean mDeferredResumePending;
|
||||
|
||||
private LauncherRootView mRootView;
|
||||
|
||||
/**
|
||||
* Create handlers to control the property changes for this activity
|
||||
*/
|
||||
|
@ -55,6 +60,23 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
|
|||
*/
|
||||
public abstract StateManager<STATE_TYPE> getStateManager();
|
||||
|
||||
protected void inflateRootView(int layoutId) {
|
||||
mRootView = (LauncherRootView) LayoutInflater.from(this).inflate(layoutId, null);
|
||||
mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LauncherRootView getRootView() {
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends View> T findViewById(int id) {
|
||||
return mRootView.findViewById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when transition to the state starts
|
||||
*/
|
||||
|
@ -87,6 +109,7 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
|
|||
* the transition if requested.
|
||||
*/
|
||||
public void reapplyUi(boolean cancelCurrentAnimation) {
|
||||
getRootView().dispatchInsets();
|
||||
getStateManager().reapplyState(cancelCurrentAnimation);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,19 @@ public class HintState extends LauncherState {
|
|||
return 80;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDepthUnchecked(Context context) {
|
||||
return 0.15f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getOverviewScrimAlpha(Launcher launcher) {
|
||||
return 0.4f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
|
||||
return new ScaleAndTranslation(0.9f, 0, 0);
|
||||
return new ScaleAndTranslation(0.92f, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SpringLoadedState extends LauncherState {
|
|||
| FLAG_HIDE_BACK_BUTTON;
|
||||
|
||||
public SpringLoadedState(int id) {
|
||||
super(id, ContainerType.OVERVIEW, STATE_FLAGS);
|
||||
super(id, ContainerType.WORKSPACE, STATE_FLAGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,27 +15,17 @@
|
|||
*/
|
||||
package com.android.launcher3.testing;
|
||||
|
||||
import static android.graphics.Bitmap.Config.ARGB_8888;
|
||||
|
||||
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Insets;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
|
@ -44,10 +34,7 @@ import com.android.launcher3.LauncherState;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -65,7 +52,6 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
protected Context mContext;
|
||||
protected DeviceProfile mDeviceProfile;
|
||||
protected LauncherAppState mLauncherAppState;
|
||||
private static LinkedList mLeaks;
|
||||
|
||||
public void init(Context context) {
|
||||
mContext = context;
|
||||
|
@ -77,15 +63,6 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
public Bundle call(String method) {
|
||||
final Bundle response = new Bundle();
|
||||
switch (method) {
|
||||
case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
|
||||
return getLauncherUIProperty(Bundle::putInt, l -> {
|
||||
final float progress = LauncherState.OVERVIEW.getVerticalProgress(l)
|
||||
- LauncherState.ALL_APPS.getVerticalProgress(l);
|
||||
final float distance = l.getAllAppsController().getShiftRange() * progress;
|
||||
return (int) distance;
|
||||
});
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
|
||||
return getLauncherUIProperty(Bundle::putInt, l -> {
|
||||
final float progress = LauncherState.NORMAL.getVerticalProgress(l)
|
||||
|
@ -99,14 +76,6 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
return getUIProperty(Bundle::putBoolean, t -> isLauncherInitialized(), () -> true);
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
|
||||
TestProtocol.sDebugTracing = true;
|
||||
break;
|
||||
|
||||
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
|
||||
TestProtocol.sDebugTracing = false;
|
||||
break;
|
||||
|
||||
case TestProtocol.REQUEST_FREEZE_APP_LIST:
|
||||
return getLauncherUIProperty(Bundle::putBoolean, l -> {
|
||||
l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
|
||||
|
@ -118,11 +87,6 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
return true;
|
||||
});
|
||||
|
||||
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
|
||||
return getLauncherUIProperty(Bundle::putInt,
|
||||
l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
|
||||
return getLauncherUIProperty(Bundle::putInt,
|
||||
l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
|
||||
|
@ -137,59 +101,19 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
}, this::getCurrentActivity);
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_PID: {
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
|
||||
runGcAndFinalizersSync();
|
||||
Debug.MemoryInfo mem = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(mem);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_JAVA_LEAK: {
|
||||
if (mLeaks == null) mLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty the memory.
|
||||
final int leakSize = 1024 * 1024;
|
||||
final byte[] bytes = new byte[leakSize];
|
||||
for (int i = 0; i < leakSize; i += 239) {
|
||||
bytes[i] = (byte) (i % 256);
|
||||
}
|
||||
mLeaks.add(bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_NATIVE_LEAK: {
|
||||
if (mLeaks == null) mLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty a bitmap.
|
||||
final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888);
|
||||
bitmap.eraseColor(Color.RED);
|
||||
mLeaks.add(bitmap);
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_VIEW_LEAK: {
|
||||
if (mLeaks == null) mLeaks = new LinkedList();
|
||||
mLeaks.add(new View(mContext));
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_ICON_HEIGHT: {
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
mDeviceProfile.allAppsCellHeightPx);
|
||||
break;
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION:
|
||||
TestProtocol.sDisableSensorRotation = true;
|
||||
break;
|
||||
return response;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
protected boolean isLauncherInitialized() {
|
||||
|
@ -201,22 +125,6 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
|
||||
}
|
||||
|
||||
private static void runGcAndFinalizersSync() {
|
||||
Runtime.getRuntime().gc();
|
||||
Runtime.getRuntime().runFinalization();
|
||||
|
||||
final CountDownLatch fence = new CountDownLatch(1);
|
||||
createFinalizationObserver(fence);
|
||||
try {
|
||||
do {
|
||||
Runtime.getRuntime().gc();
|
||||
Runtime.getRuntime().runFinalization();
|
||||
} while (!fence.await(100, TimeUnit.MILLISECONDS));
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result by getting a Launcher property on UI thread
|
||||
*/
|
||||
|
@ -257,21 +165,4 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
|||
*/
|
||||
void set(Bundle b, String key, T value);
|
||||
}
|
||||
|
||||
// Create the observer in the scope of a method to minimize the chance that
|
||||
// it remains live in a DEX/machine register at the point of the fence guard.
|
||||
// This must be kept to avoid R8 inlining it.
|
||||
@Keep
|
||||
private static void createFinalizationObserver(CountDownLatch fence) {
|
||||
new Object() {
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
fence.countDown();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,17 @@ import android.view.MotionEvent;
|
|||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class TestLogging {
|
||||
private static BiConsumer<String, String> sEventConsumer;
|
||||
|
||||
private static void recordEventSlow(String sequence, String event) {
|
||||
Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
|
||||
final BiConsumer<String, String> eventConsumer = sEventConsumer;
|
||||
if (eventConsumer != null) {
|
||||
eventConsumer.accept(sequence, event);
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordEvent(String sequence, String event) {
|
||||
|
@ -43,4 +51,8 @@ public final class TestLogging {
|
|||
recordEventSlow(sequence, message + ": " + event);
|
||||
}
|
||||
}
|
||||
|
||||
static void setEventConsumer(BiConsumer<String, String> consumer) {
|
||||
sEventConsumer = consumer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,6 +88,9 @@ public final class TestProtocol {
|
|||
public static final String REQUEST_NATIVE_LEAK = "native-leak";
|
||||
public static final String REQUEST_VIEW_LEAK = "view-leak";
|
||||
public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
|
||||
public static final String REQUEST_START_EVENT_LOGGING = "start-event-logging";
|
||||
public static final String REQUEST_GET_TEST_EVENTS = "get-test-events";
|
||||
public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
|
||||
|
||||
public static boolean sDebugTracing = false;
|
||||
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
|
||||
|
|
|
@ -21,6 +21,8 @@ import static com.android.launcher3.LauncherState.NORMAL;
|
|||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
|
||||
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
|
||||
|
@ -42,12 +44,14 @@ import com.android.launcher3.Utilities;
|
|||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.util.FlingBlockCheck;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
|
||||
|
@ -298,11 +302,11 @@ public abstract class AbstractStateChangeTouchController
|
|||
public boolean onDrag(float displacement, MotionEvent ev) {
|
||||
if (!mIsLogContainerSet) {
|
||||
if (mStartState == ALL_APPS) {
|
||||
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
|
||||
mStartContainerType = ContainerType.ALLAPPS;
|
||||
} else if (mStartState == NORMAL) {
|
||||
mStartContainerType = getLogContainerTypeForNormalState(ev);
|
||||
} else if (mStartState == OVERVIEW) {
|
||||
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
|
||||
mStartContainerType = ContainerType.TASKSWITCHER;
|
||||
}
|
||||
mIsLogContainerSet = true;
|
||||
}
|
||||
|
@ -559,10 +563,22 @@ public abstract class AbstractStateChangeTouchController
|
|||
// Transition complete. log the action
|
||||
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
|
||||
getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
|
||||
mStartContainerType,
|
||||
mStartState.containerType,
|
||||
mStartContainerType /* e.g., hotseat */,
|
||||
mStartState.containerType /* e.g., workspace */,
|
||||
targetState.containerType,
|
||||
mLauncher.getWorkspace().getCurrentPage());
|
||||
mLauncher.getStatsLogManager().logger()
|
||||
.withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
|
||||
.withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
|
||||
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
|
||||
.setWorkspace(
|
||||
LauncherAtom.WorkspaceContainer.newBuilder()
|
||||
.setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
|
||||
.build())
|
||||
.log(StatsLogManager.getLauncherAtomEvent(mStartState.containerType,
|
||||
targetState.containerType, mToState.ordinal > mFromState.ordinal
|
||||
? LAUNCHER_UNKNOWN_SWIPEUP
|
||||
: LAUNCHER_UNKNOWN_SWIPEDOWN));
|
||||
}
|
||||
|
||||
protected void clearState() {
|
||||
|
|
|
@ -113,7 +113,8 @@ public class ItemClickHandler {
|
|||
if (!folder.isOpen() && !folder.isDestroyed()) {
|
||||
// Open the requested folder
|
||||
folder.animateOpen();
|
||||
StatsLogManager.newInstance(v.getContext()).log(LAUNCHER_FOLDER_OPEN, folder.mInfo);
|
||||
StatsLogManager.newInstance(v.getContext()).logger().withItemInfo(folder.mInfo)
|
||||
.log(LAUNCHER_FOLDER_OPEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
|
|||
|
||||
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
mLauncher.getStatsLogManager().log(LAUNCHER_WORKSPACE_LONGPRESS);
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
|
||||
OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
|
||||
} else {
|
||||
cancelLongPress();
|
||||
|
|
|
@ -38,6 +38,7 @@ public class OnboardingPrefs<T extends Launcher> {
|
|||
public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
|
||||
public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
|
||||
public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
|
||||
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
|
||||
|
||||
/**
|
||||
* Events that either have happened or have not (booleans).
|
||||
|
@ -98,6 +99,13 @@ public class OnboardingPrefs<T extends Launcher> {
|
|||
return mSharedPrefs.getBoolean(key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks on-boarding preference boolean at true
|
||||
*/
|
||||
public void markChecked(String flag) {
|
||||
mSharedPrefs.edit().putBoolean(flag, true).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 1 to the given event count, if we haven't already reached the max count.
|
||||
* @return Whether we have now reached the max count.
|
||||
|
|
|
@ -86,7 +86,7 @@ public class OptionsPopupView extends ArrowPopup
|
|||
return false;
|
||||
}
|
||||
if (item.mEventId.getId() > 0) {
|
||||
mLauncher.getStatsLogManager().log(item.mEventId);
|
||||
mLauncher.getStatsLogManager().logger().log(item.mEventId);
|
||||
}
|
||||
if (item.mClickListener.onLongClick(view)) {
|
||||
close(true);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue