Snap for 11672362 from c9d07b0639 to 24Q3-release
Change-Id: I8409e07a2f4fd796e20e128ce0c40ac47140a33e
This commit is contained in:
+4
-4
@@ -75,7 +75,7 @@ android_library {
|
||||
"androidx.test.uiautomator_uiautomator",
|
||||
"androidx.preference_preference",
|
||||
"SystemUISharedLib",
|
||||
"animationlib",
|
||||
"//frameworks/libs/systemui:animationlib",
|
||||
"launcher-testing-shared",
|
||||
],
|
||||
srcs: [
|
||||
@@ -150,9 +150,9 @@ android_library {
|
||||
"androidx.cardview_cardview",
|
||||
"androidx.window_window",
|
||||
"com.google.android.material_material",
|
||||
"iconloader_base",
|
||||
"view_capture",
|
||||
"animationlib",
|
||||
"//frameworks/libs/systemui:iconloader_base",
|
||||
"//frameworks/libs/systemui:view_capture",
|
||||
"//frameworks/libs/systemui:animationlib",
|
||||
"SystemUI-statsd",
|
||||
"launcher-testing-shared",
|
||||
"androidx.lifecycle_lifecycle-common-java8",
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/overview_actions_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
@@ -36,17 +35,12 @@
|
||||
android:text="@string/action_screenshot"
|
||||
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/action_split_space"
|
||||
android:layout_width="@dimen/overview_actions_button_spacing"
|
||||
android:layout_height="1dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/action_split"
|
||||
style="@style/OverviewActionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/overview_actions_button_spacing"
|
||||
android:text="@string/action_split"
|
||||
android:theme="@style/ThemeControlHighlightWorkspaceColor"
|
||||
android:visibility="gone" />
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.wm.shell.shared.IHomeTransitionListener;
|
||||
|
||||
/**
|
||||
* Controls launcher response to home activity visibility changing.
|
||||
*/
|
||||
public class HomeTransitionController {
|
||||
|
||||
@Nullable private QuickstepLauncher mLauncher;
|
||||
@Nullable private IHomeTransitionListener mHomeTransitionListener;
|
||||
|
||||
public void registerHomeTransitionListener(QuickstepLauncher launcher) {
|
||||
mLauncher = launcher;
|
||||
mHomeTransitionListener = new IHomeTransitionListener.Stub() {
|
||||
@Override
|
||||
public void onHomeVisibilityChanged(boolean isVisible) {
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
if (mLauncher != null && mLauncher.getTaskbarUIController() != null) {
|
||||
mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(mHomeTransitionListener);
|
||||
}
|
||||
|
||||
public void unregisterHomeTransitionListener() {
|
||||
SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
|
||||
mHomeTransitionListener = null;
|
||||
mLauncher = null;
|
||||
}
|
||||
}
|
||||
@@ -35,23 +35,31 @@ import android.view.WindowInsetsController;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.dragndrop.SimpleDragLayer;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.WidgetPredictionsRequester;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.popup.PopupDataProvider;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.widget.BaseWidgetSheet;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** An Activity that can host Launcher's widget picker. */
|
||||
public class WidgetPickerActivity extends BaseActivity {
|
||||
private static final String TAG = "WidgetPickerActivity";
|
||||
|
||||
/**
|
||||
* Name of the extra that indicates that a widget being dragged.
|
||||
*
|
||||
@@ -64,14 +72,33 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
// the intent, then widgets will not be filtered for size.
|
||||
private static final String EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width";
|
||||
private static final String EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height";
|
||||
|
||||
/**
|
||||
* Widgets currently added by the user in the UI surface.
|
||||
* <p>This allows widget picker to exclude existing widgets from suggestions.</p>
|
||||
*/
|
||||
private static final String EXTRA_ADDED_APP_WIDGETS = "added_app_widgets";
|
||||
/**
|
||||
* A unique identifier of the surface hosting the widgets;
|
||||
* <p>"widgets" is reserved for home screen surface.</p>
|
||||
* <p>"widgets_hub" is reserved for glanceable hub surface.</p>
|
||||
*/
|
||||
private static final String EXTRA_UI_SURFACE = "ui_surface";
|
||||
private static final Pattern UI_SURFACE_PATTERN =
|
||||
Pattern.compile("^(widgets|widgets_hub)$");
|
||||
private SimpleDragLayer<WidgetPickerActivity> mDragLayer;
|
||||
private WidgetsModel mModel;
|
||||
private LauncherAppState mApp;
|
||||
private WidgetPredictionsRequester mWidgetPredictionsRequester;
|
||||
private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
|
||||
|
||||
private int mDesiredWidgetWidth;
|
||||
private int mDesiredWidgetHeight;
|
||||
private int mWidgetCategoryFilter;
|
||||
@Nullable
|
||||
private String mUiSurface;
|
||||
// Widgets existing on the host surface.
|
||||
@NonNull
|
||||
private List<AppWidgetProviderInfo> mAddedWidgets = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -80,9 +107,8 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
|
||||
|
||||
LauncherAppState app = LauncherAppState.getInstance(this);
|
||||
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
|
||||
|
||||
mApp = LauncherAppState.getInstance(this);
|
||||
InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
|
||||
mDeviceProfile = idp.getDeviceProfile(this);
|
||||
mModel = new WidgetsModel();
|
||||
|
||||
@@ -97,6 +123,11 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
widgetSheet.disableNavBarScrim(true);
|
||||
widgetSheet.addOnCloseListener(this::finish);
|
||||
|
||||
parseIntentExtras();
|
||||
refreshAndBindWidgets();
|
||||
}
|
||||
|
||||
private void parseIntentExtras() {
|
||||
// A value of 0 for either size means that no filtering will occur in that dimension. If
|
||||
// both values are 0, then no size filtering will occur.
|
||||
mDesiredWidgetWidth =
|
||||
@@ -108,7 +139,15 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
mWidgetCategoryFilter =
|
||||
getIntent().getIntExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, 0);
|
||||
|
||||
refreshAndBindWidgets();
|
||||
String uiSurfaceParam = getIntent().getStringExtra(EXTRA_UI_SURFACE);
|
||||
if (uiSurfaceParam != null && UI_SURFACE_PATTERN.matcher(uiSurfaceParam).matches()) {
|
||||
mUiSurface = uiSurfaceParam;
|
||||
}
|
||||
ArrayList<AppWidgetProviderInfo> addedWidgets = getIntent().getParcelableArrayListExtra(
|
||||
EXTRA_ADDED_APP_WIDGETS, AppWidgetProviderInfo.class);
|
||||
if (addedWidgets != null) {
|
||||
mAddedWidgets = addedWidgets;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -179,11 +218,12 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
};
|
||||
}
|
||||
|
||||
/** Updates the model with widgets and provides them after applying the provided filter. */
|
||||
private void refreshAndBindWidgets() {
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
LauncherAppState app = LauncherAppState.getInstance(this);
|
||||
mModel.update(app, null);
|
||||
final ArrayList<WidgetsListBaseEntry> widgets =
|
||||
final List<WidgetsListBaseEntry> allWidgets =
|
||||
mModel.getFilteredWidgetsListForPicker(
|
||||
app.getContext(),
|
||||
/*widgetItemFilter=*/ widget -> {
|
||||
@@ -193,10 +233,37 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
return verdict.isAcceptable;
|
||||
}
|
||||
);
|
||||
MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
|
||||
bindWidgets(allWidgets);
|
||||
if (mUiSurface != null) {
|
||||
Map<PackageUserKey, List<WidgetItem>> allWidgetsMap = allWidgets.stream()
|
||||
.filter(WidgetsListHeaderEntry.class::isInstance)
|
||||
.collect(Collectors.toMap(
|
||||
entry -> PackageUserKey.fromPackageItemInfo(entry.mPkgItem),
|
||||
entry -> entry.mWidgets)
|
||||
);
|
||||
mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(),
|
||||
mUiSurface, allWidgetsMap);
|
||||
mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void bindWidgets(List<WidgetsListBaseEntry> widgets) {
|
||||
MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
|
||||
}
|
||||
|
||||
private void bindRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
|
||||
MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setRecommendedWidgets(recommendedWidgets));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mWidgetPredictionsRequester != null) {
|
||||
mWidgetPredictionsRequester.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private WidgetAcceptabilityVerdict isWidgetAcceptable(WidgetItem widget) {
|
||||
final AppWidgetProviderInfo info = widget.widgetInfo;
|
||||
if (info == null) {
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppPredictionManager;
|
||||
import android.app.prediction.AppPredictor;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Works with app predictor to fetch and process widget predictions displayed in a standalone
|
||||
* widget picker activity for a UI surface.
|
||||
*/
|
||||
public class WidgetPredictionsRequester {
|
||||
private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
|
||||
private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
|
||||
|
||||
@Nullable
|
||||
private AppPredictor mAppPredictor;
|
||||
private final Context mContext;
|
||||
@NonNull
|
||||
private final String mUiSurface;
|
||||
@NonNull
|
||||
private final Map<PackageUserKey, List<WidgetItem>> mAllWidgets;
|
||||
|
||||
public WidgetPredictionsRequester(Context context, @NonNull String uiSurface,
|
||||
@NonNull Map<PackageUserKey, List<WidgetItem>> allWidgets) {
|
||||
mContext = context;
|
||||
mUiSurface = uiSurface;
|
||||
mAllWidgets = Collections.unmodifiableMap(allWidgets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests predictions from the app predictions manager and registers the provided callback to
|
||||
* receive updates when predictions are available.
|
||||
*
|
||||
* @param existingWidgets widgets that are currently added to the surface;
|
||||
* @param callback consumer of prediction results to be called when predictions are
|
||||
* available
|
||||
*/
|
||||
public void request(List<AppWidgetProviderInfo> existingWidgets,
|
||||
Consumer<List<ItemInfo>> callback) {
|
||||
Bundle bundle = buildBundleForPredictionSession(existingWidgets, mUiSurface);
|
||||
Predicate<WidgetItem> filter = notOnUiSurfaceFilter(existingWidgets);
|
||||
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
clear();
|
||||
AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
|
||||
if (apm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAppPredictor = apm.createAppPredictionSession(
|
||||
new AppPredictionContext.Builder(mContext)
|
||||
.setUiSurface(mUiSurface)
|
||||
.setExtras(bundle)
|
||||
.setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
|
||||
.build());
|
||||
mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR,
|
||||
targets -> bindPredictions(targets, filter, callback));
|
||||
mAppPredictor.requestPredictionUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bundle that can be passed in a prediction session
|
||||
*
|
||||
* @param addedWidgets widgets that are already added by the user in the ui surface
|
||||
* @param uiSurface a unique identifier of the surface hosting widgets; format
|
||||
* "widgets_xx"; note - "widgets" is reserved for home screen surface.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static Bundle buildBundleForPredictionSession(List<AppWidgetProviderInfo> addedWidgets,
|
||||
String uiSurface) {
|
||||
Bundle bundle = new Bundle();
|
||||
ArrayList<AppTargetEvent> addedAppTargetEvents = new ArrayList<>();
|
||||
for (AppWidgetProviderInfo info : addedWidgets) {
|
||||
ComponentName componentName = info.provider;
|
||||
AppTargetEvent appTargetEvent = buildAppTargetEvent(uiSurface, info, componentName);
|
||||
addedAppTargetEvents.add(appTargetEvent);
|
||||
}
|
||||
bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, addedAppTargetEvents);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the AppTargetEvent for added widgets in a form that can be passed to the widget
|
||||
* predictor.
|
||||
* Also see {@link PredictionHelper}
|
||||
*/
|
||||
private static AppTargetEvent buildAppTargetEvent(String uiSurface, AppWidgetProviderInfo info,
|
||||
ComponentName componentName) {
|
||||
AppTargetId appTargetId = new AppTargetId("widget:" + componentName.getPackageName());
|
||||
AppTarget appTarget = new AppTarget.Builder(appTargetId, componentName.getPackageName(),
|
||||
/*user=*/ info.getProfile()).setClassName(componentName.getClassName()).build();
|
||||
return new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_PIN)
|
||||
.setLaunchLocation(uiSurface).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filter to match {@link WidgetItem}s that don't exist on the UI surface.
|
||||
*/
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
static Predicate<WidgetItem> notOnUiSurfaceFilter(
|
||||
List<AppWidgetProviderInfo> existingWidgets) {
|
||||
Set<ComponentKey> existingComponentKeys = existingWidgets.stream().map(
|
||||
widget -> new ComponentKey(widget.provider, widget.getProfile())).collect(
|
||||
Collectors.toSet());
|
||||
return widgetItem -> !existingComponentKeys.contains(widgetItem);
|
||||
}
|
||||
|
||||
/** Provides the predictions returned by the predictor to the registered callback. */
|
||||
@WorkerThread
|
||||
private void bindPredictions(List<AppTarget> targets, Predicate<WidgetItem> filter,
|
||||
Consumer<List<ItemInfo>> callback) {
|
||||
List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, filter);
|
||||
List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
|
||||
|
||||
MAIN_EXECUTOR.execute(() -> callback.accept(mappedPredictions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the provided filter (e.g. widgets not on workspace) on the predictions returned by
|
||||
* the predictor.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static List<WidgetItem> filterPredictions(List<AppTarget> predictions,
|
||||
Map<PackageUserKey, List<WidgetItem>> allWidgets, Predicate<WidgetItem> filter) {
|
||||
List<WidgetItem> servicePredictedItems = new ArrayList<>();
|
||||
List<WidgetItem> localFilteredWidgets = new ArrayList<>();
|
||||
|
||||
for (AppTarget prediction : predictions) {
|
||||
List<WidgetItem> widgetsInPackage = allWidgets.get(
|
||||
new PackageUserKey(prediction.getPackageName(), prediction.getUser()));
|
||||
if (widgetsInPackage == null || widgetsInPackage.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String className = prediction.getClassName();
|
||||
if (!TextUtils.isEmpty(className)) {
|
||||
WidgetItem item = widgetsInPackage.stream()
|
||||
.filter(w -> className.equals(w.componentName.getClassName()))
|
||||
.filter(filter)
|
||||
.findFirst().orElse(null);
|
||||
if (item != null) {
|
||||
servicePredictedItems.add(item);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// No widget was added by the service, try local filtering
|
||||
widgetsInPackage.stream().filter(filter).findFirst()
|
||||
.ifPresent(localFilteredWidgets::add);
|
||||
}
|
||||
if (servicePredictedItems.isEmpty()) {
|
||||
servicePredictedItems.addAll(localFilteredWidgets);
|
||||
}
|
||||
|
||||
return servicePredictedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the list of {@link WidgetItem}s to the list of {@link ItemInfo}s.
|
||||
*/
|
||||
private List<ItemInfo> mapWidgetItemsToItemInfo(List<WidgetItem> widgetItems) {
|
||||
List<ItemInfo> items;
|
||||
if (enableCategorizedWidgetSuggestions()) {
|
||||
WidgetRecommendationCategoryProvider categoryProvider =
|
||||
WidgetRecommendationCategoryProvider.newInstance(mContext);
|
||||
items = widgetItems.stream()
|
||||
.map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
|
||||
categoryProvider.getWidgetRecommendationCategory(mContext, it)))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
items = widgetItems.stream().map(it -> new PendingAddWidgetInfo(it.widgetInfo,
|
||||
CONTAINER_WIDGETS_PREDICTION)).collect(Collectors.toList());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/** Cleans up any open prediction sessions. */
|
||||
public void clear() {
|
||||
if (mAppPredictor != null) {
|
||||
mAppPredictor.destroy();
|
||||
mAppPredictor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.os.Debug;
|
||||
import android.os.SystemProperties;
|
||||
@@ -136,9 +135,6 @@ public class DesktopVisibilityController {
|
||||
Log.d(TAG, "setVisibleFreeformTasksCount: visibleTasksCount=" + visibleTasksCount
|
||||
+ " currentValue=" + mVisibleFreeformTasksCount);
|
||||
}
|
||||
if (!enableDesktopWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visibleTasksCount != mVisibleFreeformTasksCount) {
|
||||
final boolean wasVisible = mVisibleFreeformTasksCount > 0;
|
||||
@@ -180,9 +176,6 @@ public class DesktopVisibilityController {
|
||||
Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
|
||||
+ " currentValue=" + mInOverviewState);
|
||||
}
|
||||
if (!enableDesktopWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
if (overviewStateEnabled != mInOverviewState) {
|
||||
mInOverviewState = overviewStateEnabled;
|
||||
if (mInOverviewState) {
|
||||
@@ -202,9 +195,6 @@ public class DesktopVisibilityController {
|
||||
Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
|
||||
+ " currentValue=" + mBackgroundStateEnabled);
|
||||
}
|
||||
if (!enableDesktopWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
if (backgroundStateEnabled != mBackgroundStateEnabled) {
|
||||
mBackgroundStateEnabled = backgroundStateEnabled;
|
||||
if (mBackgroundStateEnabled) {
|
||||
@@ -229,9 +219,6 @@ public class DesktopVisibilityController {
|
||||
* Notify controller that recents gesture has started.
|
||||
*/
|
||||
public void setRecentsGestureStart() {
|
||||
if (!enableDesktopWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setRecentsGestureStart");
|
||||
}
|
||||
@@ -243,9 +230,6 @@ public class DesktopVisibilityController {
|
||||
* {@link com.android.quickstep.GestureState.GestureEndTarget}
|
||||
*/
|
||||
public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
|
||||
if (!enableDesktopWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setRecentsGestureEnd: endTarget=" + endTarget);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.android.launcher3.taskbar;
|
||||
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
@@ -117,9 +115,7 @@ public final class KeyboardQuickSwitchController implements
|
||||
DesktopVisibilityController desktopController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
final boolean onDesktop =
|
||||
enableDesktopWindowingMode()
|
||||
&& desktopController != null
|
||||
&& desktopController.areFreeformTasksVisible();
|
||||
desktopController != null && desktopController.areFreeformTasksVisible();
|
||||
|
||||
if (mModel.isTaskListValid(mTaskListChangeId)) {
|
||||
// When we are opening the KQS with no focus override, check if the first task is
|
||||
@@ -158,14 +154,12 @@ public final class KeyboardQuickSwitchController implements
|
||||
|
||||
// Hide all desktop tasks and show them on the hidden tile
|
||||
int hiddenDesktopTasks = 0;
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopTask desktopTask = findDesktopTask(tasks);
|
||||
if (desktopTask != null) {
|
||||
hiddenDesktopTasks = desktopTask.tasks.size();
|
||||
tasks = tasks.stream()
|
||||
.filter(t -> !(t instanceof DesktopTask))
|
||||
.collect(Collectors.toCollection(ArrayList<GroupTask>::new));
|
||||
}
|
||||
DesktopTask desktopTask = findDesktopTask(tasks);
|
||||
if (desktopTask != null) {
|
||||
hiddenDesktopTasks = desktopTask.tasks.size();
|
||||
tasks = tasks.stream()
|
||||
.filter(t -> !(t instanceof DesktopTask))
|
||||
.collect(Collectors.toCollection(ArrayList<GroupTask>::new));
|
||||
}
|
||||
mTasks = tasks.stream()
|
||||
.limit(MAX_TASKS)
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
|
||||
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
|
||||
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
|
||||
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
@@ -35,6 +34,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Flags;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.QuickstepTransitionManager;
|
||||
import com.android.launcher3.R;
|
||||
@@ -49,8 +49,10 @@ import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.MultiPropertyFactory;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.quickstep.HomeVisibilityState;
|
||||
import com.android.quickstep.LauncherActivityInterface;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.TISBindHelper;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -79,6 +81,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
|
||||
|
||||
private final QuickstepLauncher mLauncher;
|
||||
private final HomeVisibilityState mHomeState;
|
||||
|
||||
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
|
||||
dp -> {
|
||||
@@ -87,6 +90,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
mControllers.taskbarViewController.onRotationChanged(dp);
|
||||
}
|
||||
};
|
||||
private final HomeVisibilityState.VisibilityChangeListener mVisibilityChangeListener =
|
||||
this::onLauncherVisibilityChanged;
|
||||
|
||||
// Initialized in init.
|
||||
private final TaskbarLauncherStateController
|
||||
@@ -94,6 +99,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
public LauncherTaskbarUIController(QuickstepLauncher launcher) {
|
||||
mLauncher = launcher;
|
||||
mHomeState = SystemUiProxy.INSTANCE.get(mLauncher).getHomeVisibilityState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,8 +110,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
mControllers.getSharedState().sysuiStateFlags);
|
||||
|
||||
mLauncher.setTaskbarUIController(this);
|
||||
|
||||
onLauncherVisibilityChanged(mLauncher.hasBeenResumed(), true /* fromInit */);
|
||||
mHomeState.addListener(mVisibilityChangeListener);
|
||||
onLauncherVisibilityChanged(
|
||||
Flags.useActivityOverlay()
|
||||
? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed(),
|
||||
true /* fromInit */);
|
||||
|
||||
onStashedInAppChanged(mLauncher.getDeviceProfile());
|
||||
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
|
||||
@@ -129,6 +138,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
mLauncher.setTaskbarUIController(null);
|
||||
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
|
||||
mHomeState.removeListener(mVisibilityChangeListener);
|
||||
updateTaskTransitionSpec(true);
|
||||
}
|
||||
|
||||
@@ -209,9 +219,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
DesktopVisibilityController desktopController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
final boolean onDesktop =
|
||||
enableDesktopWindowingMode()
|
||||
&& desktopController != null
|
||||
&& desktopController.areFreeformTasksVisible();
|
||||
desktopController != null && desktopController.areFreeformTasksVisible();
|
||||
if (onDesktop) {
|
||||
isVisible = false;
|
||||
}
|
||||
@@ -234,7 +242,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
@Override
|
||||
public void refreshResumedState() {
|
||||
onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
|
||||
onLauncherVisibilityChanged(Flags.useActivityOverlay()
|
||||
? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -656,7 +656,8 @@ public class TaskbarLauncherStateController {
|
||||
* Returns if the current Launcher state has hotseat on top of other elemnets.
|
||||
*/
|
||||
public boolean isInHotseatOnTopStates() {
|
||||
return mLauncherState != LauncherState.ALL_APPS;
|
||||
return mLauncherState != LauncherState.ALL_APPS
|
||||
&& !mLauncher.getWorkspace().isOverlayShown();
|
||||
}
|
||||
|
||||
boolean isInOverview() {
|
||||
|
||||
@@ -27,7 +27,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
|
||||
@@ -281,12 +280,10 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa
|
||||
private void navigateHome() {
|
||||
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
|
||||
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
desktopVisibilityController.onHomeActionTriggered();
|
||||
}
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
desktopVisibilityController.onHomeActionTriggered();
|
||||
}
|
||||
|
||||
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
|
||||
|
||||
@@ -118,7 +118,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
|
||||
FolderInfo fi = (FolderInfo) info;
|
||||
if (fi.anyMatch(matcher)) {
|
||||
FolderDotInfo folderDotInfo = new FolderDotInfo();
|
||||
for (WorkspaceItemInfo si : fi.getContents()) {
|
||||
for (ItemInfo si : fi.getContents()) {
|
||||
folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
|
||||
}
|
||||
((FolderIcon) v).setDotInfo(folderDotInfo);
|
||||
|
||||
@@ -207,10 +207,10 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScaleProgressChanged() {
|
||||
super.onScaleProgressChanged();
|
||||
mAppsView.setClipChildren(!mIsBackProgressing);
|
||||
mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsBackProgressing);
|
||||
protected void onUserSwipeToDismissProgressChanged() {
|
||||
super.onUserSwipeToDismissProgressChanged();
|
||||
mAppsView.setClipChildren(!mIsDismissInProgress);
|
||||
mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsDismissInProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -264,7 +264,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla
|
||||
if (mAllAppsCallbacks.handleSearchBackInvoked()) {
|
||||
// We need to scale back taskbar all apps if we navigate back within search inside all
|
||||
// apps
|
||||
animateSlideInViewToNoScale();
|
||||
animateSwipeToDismissProgressToStart();
|
||||
} else {
|
||||
super.onBackInvoked();
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
mBubbleStashedHandleViewController.setHiddenForBubbles(
|
||||
!sBubbleBarEnabled || mBubbles.isEmpty());
|
||||
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
|
||||
key -> setSelectedBubble(mBubbles.get(key)));
|
||||
key -> setSelectedBubbleInternal(mBubbles.get(key)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
if (bubbleToSelect != null) {
|
||||
setSelectedBubble(bubbleToSelect);
|
||||
setSelectedBubbleInternal(bubbleToSelect);
|
||||
if (previouslySelectedBubble == null) {
|
||||
mBubbleStashController.animateToInitialState(update.expanded);
|
||||
}
|
||||
@@ -409,8 +409,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
|
||||
// Animate when receiving updates. Skip it if we received the initial state.
|
||||
boolean animate = !update.initialState;
|
||||
mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
|
||||
mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
|
||||
updateBubbleBarLocationInternal(update.bubbleBarLocation, animate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,7 +435,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
/** Updates the currently selected bubble for launcher views and tells WMShell to show it. */
|
||||
public void showAndSelectBubble(BubbleBarItem b) {
|
||||
if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey());
|
||||
setSelectedBubble(b);
|
||||
setSelectedBubbleInternal(b);
|
||||
showSelectedBubble();
|
||||
}
|
||||
|
||||
@@ -445,7 +444,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
* WMShell that the selection has changed, that should go through either
|
||||
* {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}.
|
||||
*/
|
||||
private void setSelectedBubble(BubbleBarItem b) {
|
||||
private void setSelectedBubbleInternal(BubbleBarItem b) {
|
||||
if (!Objects.equals(b, mSelectedBubble)) {
|
||||
if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
|
||||
mSelectedBubble = b;
|
||||
@@ -464,6 +463,21 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new bubble bar location.
|
||||
* <p>
|
||||
* Updates the value locally in Launcher and in WMShell.
|
||||
*/
|
||||
public void updateBubbleBarLocation(BubbleBarLocation location) {
|
||||
updateBubbleBarLocationInternal(location, false /* animate */);
|
||||
mSystemUiProxy.setBubbleBarLocation(location);
|
||||
}
|
||||
|
||||
private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) {
|
||||
mBubbleBarViewController.setBubbleBarLocation(location, animate);
|
||||
mBubbleStashController.setBubbleBarLocation(location);
|
||||
}
|
||||
|
||||
//
|
||||
// Loading data for the bubbles
|
||||
//
|
||||
|
||||
@@ -24,7 +24,9 @@ import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.LayoutDirection;
|
||||
@@ -108,6 +110,7 @@ public class BubbleBarView extends FrameLayout {
|
||||
private final float mIconSize;
|
||||
// The elevation of the bubbles within the bar
|
||||
private final float mBubbleElevation;
|
||||
private final float mDragElevation;
|
||||
private final int mPointerSize;
|
||||
|
||||
// Whether the bar is expanded (i.e. the bubble activity is being displayed).
|
||||
@@ -138,11 +141,15 @@ public class BubbleBarView extends FrameLayout {
|
||||
@Nullable
|
||||
private Consumer<String> mUpdateSelectedBubbleAfterCollapse;
|
||||
|
||||
private boolean mDragging;
|
||||
|
||||
@Nullable
|
||||
private BubbleView mDraggedBubbleView;
|
||||
|
||||
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
|
||||
|
||||
private boolean mLocationChangePending;
|
||||
|
||||
public BubbleBarView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -163,6 +170,7 @@ public class BubbleBarView extends FrameLayout {
|
||||
mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
|
||||
mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
|
||||
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
|
||||
mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
|
||||
mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);
|
||||
|
||||
setClipToPadding(false);
|
||||
@@ -237,12 +245,13 @@ public class BubbleBarView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
private void onBubbleBarLocationChanged() {
|
||||
mLocationChangePending = false;
|
||||
final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
|
||||
mBubbleBarBackground.setAnchorLeft(onLeft);
|
||||
mRelativePivotX = onLeft ? 0f : 1f;
|
||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
if (layoutParams instanceof LayoutParams lp) {
|
||||
if (getLayoutParams() instanceof LayoutParams lp) {
|
||||
lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
|
||||
setLayoutParams(lp);
|
||||
}
|
||||
@@ -267,11 +276,82 @@ public class BubbleBarView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this view is being currently being dragged
|
||||
*/
|
||||
public void setIsDragging(boolean dragging) {
|
||||
if (mDragging == dragging) {
|
||||
return;
|
||||
}
|
||||
mDragging = dragging;
|
||||
setElevation(dragging ? mDragElevation : mBubbleElevation);
|
||||
if (!dragging && mLocationChangePending) {
|
||||
// During drag finish animation we may update the translation x value to shift the
|
||||
// bubble to the new drop target. Clear the translation here.
|
||||
setTranslationX(0f);
|
||||
onBubbleBarLocationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust resting position for the bubble bar while it is being dragged.
|
||||
* <p>
|
||||
* Bubble bar is laid out on left or right side of the screen. When it is being dragged to
|
||||
* the opposite side, the resting position should be on that side. Calculate any additional
|
||||
* translation that may be required to move the bubble bar to the new side.
|
||||
*
|
||||
* @param restingPosition relative resting position of the bubble bar from the laid out position
|
||||
*/
|
||||
@SuppressLint("RtlHardcoded")
|
||||
void adjustRelativeRestingPosition(PointF restingPosition) {
|
||||
final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
|
||||
// Bubble bar is placed left or right with gravity. Check where it is currently.
|
||||
final int absoluteGravity = Gravity.getAbsoluteGravity(
|
||||
((LayoutParams) getLayoutParams()).gravity, getLayoutDirection());
|
||||
final boolean gravityOnLeft =
|
||||
(absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT;
|
||||
|
||||
// Bubble bar is pinned to the same side per gravity and the desired location.
|
||||
// Resting translation does not need to be adjusted.
|
||||
if (locationOnLeft == gravityOnLeft) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bubble bar is laid out on left or right side of the screen. And the desired new
|
||||
// location is on the other side. Calculate x translation value required to shift the
|
||||
// bubble bar from one side to the other.
|
||||
float x = getDistanceFromOtherSide();
|
||||
if (locationOnLeft) {
|
||||
// New location is on the left, shift left
|
||||
// before -> |......ooo.| after -> |.ooo......|
|
||||
restingPosition.x = -x;
|
||||
} else {
|
||||
// New location is on the right, shift right
|
||||
// before -> |.ooo......| after -> |......ooo.|
|
||||
restingPosition.x = x;
|
||||
}
|
||||
}
|
||||
|
||||
private float getDistanceFromOtherSide() {
|
||||
// Calculate the shift needed to position the bubble bar on the other side
|
||||
int displayWidth = getResources().getDisplayMetrics().widthPixels;
|
||||
int margin = 0;
|
||||
if (getLayoutParams() instanceof MarginLayoutParams lp) {
|
||||
margin += lp.leftMargin;
|
||||
margin += lp.rightMargin;
|
||||
}
|
||||
return (float) (displayWidth - getWidth() - margin);
|
||||
}
|
||||
|
||||
private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
|
||||
if (bubbleBarLocation != mBubbleBarLocation) {
|
||||
mBubbleBarLocation = bubbleBarLocation;
|
||||
onBubbleBarLocationChanged();
|
||||
invalidate();
|
||||
if (mDragging) {
|
||||
mLocationChangePending = true;
|
||||
} else {
|
||||
onBubbleBarLocationChanged();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.view.ViewConfiguration;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.taskbar.TaskbarActivityContext;
|
||||
|
||||
/**
|
||||
@@ -55,9 +54,8 @@ public class BubbleDragController {
|
||||
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
|
||||
mBubbleDismissController = bubbleControllers.bubbleDismissController;
|
||||
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
|
||||
mBubbleBarPinController.setListener(location -> {
|
||||
// TODO(b/330585397): update bubble bar location in shell
|
||||
});
|
||||
mBubbleBarPinController.setListener(
|
||||
bubbleControllers.bubbleBarController::updateBubbleBarLocation);
|
||||
mBubbleDismissController.setListener(
|
||||
stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
|
||||
}
|
||||
@@ -96,11 +94,8 @@ public class BubbleDragController {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) {
|
||||
PointF initialRelativePivot = new PointF();
|
||||
final int restingElevation = bubbleBarView.getResources().getDimensionPixelSize(
|
||||
R.dimen.bubblebar_elevation);
|
||||
final int dragElevation = bubbleBarView.getResources().getDimensionPixelSize(
|
||||
R.dimen.bubblebar_drag_elevation);
|
||||
bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
|
||||
|
||||
@Override
|
||||
protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
|
||||
if (bubbleBarView.isExpanded()) return false;
|
||||
@@ -114,7 +109,7 @@ public class BubbleDragController {
|
||||
// By default the bubble bar view pivot is in bottom right corner, while dragging
|
||||
// it should be centered in order to align it with the dismiss target view
|
||||
bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
|
||||
bubbleBarView.setElevation(dragElevation);
|
||||
bubbleBarView.setIsDragging(true);
|
||||
mBubbleBarPinController.onDragStart(
|
||||
bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
|
||||
}
|
||||
@@ -138,7 +133,14 @@ public class BubbleDragController {
|
||||
void onDragEnd() {
|
||||
// Restoring the initial pivot for the bubble bar view
|
||||
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
|
||||
bubbleBarView.setElevation(restingElevation);
|
||||
bubbleBarView.setIsDragging(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PointF getRestingPosition() {
|
||||
PointF restingPosition = super.getRestingPosition();
|
||||
bubbleBarView.adjustRelativeRestingPosition(restingPosition);
|
||||
return restingPosition;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -226,6 +228,13 @@ public class BubbleDragController {
|
||||
protected void onDragDismiss() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resting position of the view when drag is released
|
||||
*/
|
||||
protected PointF getRestingPosition() {
|
||||
return mViewInitialPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
|
||||
@@ -353,7 +362,7 @@ public class BubbleDragController {
|
||||
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
|
||||
} else {
|
||||
onDragRelease();
|
||||
mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(),
|
||||
mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
|
||||
onComplete);
|
||||
}
|
||||
mBubbleDismissController.hideDismissView();
|
||||
|
||||
@@ -62,8 +62,8 @@ import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MOD
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
|
||||
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -105,7 +105,6 @@ import com.android.app.viewcapture.SettingsAwareViewCapture;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Flags;
|
||||
import com.android.launcher3.HomeTransitionController;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
@@ -217,7 +216,7 @@ public class QuickstepLauncher extends Launcher {
|
||||
private FixedContainerItems mAllAppsPredictions;
|
||||
private HotseatPredictionController mHotseatPredictionController;
|
||||
private DepthController mDepthController;
|
||||
private DesktopVisibilityController mDesktopVisibilityController;
|
||||
private @Nullable DesktopVisibilityController mDesktopVisibilityController;
|
||||
private QuickstepTransitionManager mAppTransitionManager;
|
||||
private OverviewActionsView mActionsView;
|
||||
private TISBindHelper mTISBindHelper;
|
||||
@@ -245,8 +244,6 @@ public class QuickstepLauncher extends Launcher {
|
||||
|
||||
private boolean mIsPredictiveBackToHomeInProgress;
|
||||
|
||||
private HomeTransitionController mHomeTransitionController;
|
||||
|
||||
@Override
|
||||
protected void setupViews() {
|
||||
super.setupViews();
|
||||
@@ -277,15 +274,10 @@ public class QuickstepLauncher extends Launcher {
|
||||
mAppTransitionManager.registerRemoteAnimations();
|
||||
mAppTransitionManager.registerRemoteTransitions();
|
||||
|
||||
if (FeatureFlags.enableHomeTransitionListener()) {
|
||||
mHomeTransitionController = new HomeTransitionController();
|
||||
mHomeTransitionController.registerHomeTransitionListener(this);
|
||||
}
|
||||
|
||||
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
|
||||
mDepthController = new DepthController(this);
|
||||
mDesktopVisibilityController = new DesktopVisibilityController(this);
|
||||
if (enableDesktopWindowingMode()) {
|
||||
mDesktopVisibilityController = new DesktopVisibilityController(this);
|
||||
mDesktopVisibilityController.registerSystemUiListener();
|
||||
mSplitSelectStateController.initSplitFromDesktopController(this);
|
||||
}
|
||||
@@ -523,10 +515,6 @@ public class QuickstepLauncher extends Launcher {
|
||||
mLauncherUnfoldAnimationController.onDestroy();
|
||||
}
|
||||
|
||||
if (mHomeTransitionController != null) {
|
||||
mHomeTransitionController.unregisterHomeTransitionListener();
|
||||
}
|
||||
|
||||
if (mDesktopVisibilityController != null) {
|
||||
mDesktopVisibilityController.unregisterSystemUiListener();
|
||||
}
|
||||
@@ -948,15 +936,13 @@ public class QuickstepLauncher extends Launcher {
|
||||
|
||||
@Override
|
||||
public void setResumed() {
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopVisibilityController controller = mDesktopVisibilityController;
|
||||
if (controller != null && controller.areFreeformTasksVisible()
|
||||
&& !controller.isRecentsGestureInProgress()) {
|
||||
// Return early to skip setting activity to appear as resumed
|
||||
// TODO(b/255649902): shouldn't be needed when we have a separate launcher state
|
||||
// for desktop that we can use to control other parts of launcher
|
||||
return;
|
||||
}
|
||||
if (mDesktopVisibilityController != null
|
||||
&& mDesktopVisibilityController.areFreeformTasksVisible()
|
||||
&& !mDesktopVisibilityController.isRecentsGestureInProgress()) {
|
||||
// Return early to skip setting activity to appear as resumed
|
||||
// TODO(b/255649902): shouldn't be needed when we have a separate launcher state
|
||||
// for desktop that we can use to control other parts of launcher
|
||||
return;
|
||||
}
|
||||
super.setResumed();
|
||||
}
|
||||
@@ -1090,6 +1076,7 @@ public class QuickstepLauncher extends Launcher {
|
||||
return mDepthController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DesktopVisibilityController getDesktopVisibilityController() {
|
||||
return mDesktopVisibilityController;
|
||||
}
|
||||
|
||||
@@ -68,10 +68,13 @@ public class AllAppsState extends LauncherState {
|
||||
@Override
|
||||
public void onBackInvoked(Launcher launcher) {
|
||||
// In predictive back swipe, onBackInvoked() will be called after onBackStarted().
|
||||
// Because the 2nd InteractionJankMonitor.begin() will be ignore within timeout, it's safe
|
||||
// to call InteractionJankMonitorWrapper.begin here.
|
||||
InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
|
||||
Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
|
||||
// In 3 button mode, onBackStarted() is not called but onBackInvoked() will be called.
|
||||
// Thus In onBackInvoked(), we should only begin instrumenting if we didn't call
|
||||
// onBackStarted() to start instrumenting CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK.
|
||||
if (!InteractionJankMonitorWrapper.isInstrumenting(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK)) {
|
||||
InteractionJankMonitorWrapper.begin(
|
||||
launcher.getAppsView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
|
||||
}
|
||||
super.onBackInvoked(launcher);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.uioverrides.states;
|
||||
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
||||
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
@@ -92,8 +91,7 @@ public class BackgroundAppState extends OverviewState {
|
||||
|
||||
@Override
|
||||
protected float getDepthUnchecked(Context context) {
|
||||
if (enableDesktopWindowingMode()
|
||||
&& Launcher.getLauncher(context).areFreeformTasksVisible()) {
|
||||
if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
|
||||
// Don't blur the background while freeform tasks are visible
|
||||
return BaseDepthController.DEPTH_0_PERCENT;
|
||||
} else if (enableScalingRevealHomeAnimation()) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.android.launcher3.uioverrides.states;
|
||||
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
@@ -46,11 +45,9 @@ public class QuickSwitchState extends BackgroundAppState {
|
||||
|
||||
@Override
|
||||
public int getWorkspaceScrimColor(Launcher launcher) {
|
||||
if (enableDesktopWindowingMode()) {
|
||||
if (launcher.areFreeformTasksVisible()) {
|
||||
// No scrim while freeform tasks are visible
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
if (launcher.areFreeformTasksVisible()) {
|
||||
// No scrim while freeform tasks are visible
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
DeviceProfile dp = launcher.getDeviceProfile();
|
||||
if (dp.isTaskbarPresentInApps) {
|
||||
|
||||
@@ -61,7 +61,6 @@ import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -947,7 +946,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
public void onRecentsAnimationStart(RecentsAnimationController controller,
|
||||
RecentsAnimationTargets targets) {
|
||||
super.onRecentsAnimationStart(controller, targets);
|
||||
if (enableDesktopWindowingMode() && targets.hasDesktopTasks()) {
|
||||
if (targets.hasDesktopTasks()) {
|
||||
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
|
||||
} else {
|
||||
int untrimmedAppCount = mRemoteTargetHandles.length;
|
||||
@@ -1170,13 +1169,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
|
||||
// Notify the SysUI to use fade-in animation when entering PiP
|
||||
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
mActivityInterface.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
// Notify the SysUI to stash desktop apps if they are visible
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
mActivityInterface.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
desktopVisibilityController.onHomeActionTriggered();
|
||||
}
|
||||
desktopVisibilityController.onHomeActionTriggered();
|
||||
}
|
||||
break;
|
||||
case RECENTS:
|
||||
|
||||
@@ -25,7 +25,6 @@ import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
|
||||
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
|
||||
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
|
||||
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
|
||||
@@ -109,19 +108,17 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
if (endTarget != null) {
|
||||
// We were on our way to this state when we got canceled, end there instead.
|
||||
startState = stateFromGestureEndTarget(endTarget);
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopVisibilityController controller = getDesktopVisibilityController();
|
||||
if (controller != null && controller.areFreeformTasksVisible()
|
||||
&& endTarget == LAST_TASK) {
|
||||
// When we are cancelling the transition and going back to last task, move to
|
||||
// rest state instead when desktop tasks are visible.
|
||||
// If a fullscreen task is visible, launcher goes to normal state when the
|
||||
// activity is stopped. This does not happen when freeform tasks are visible
|
||||
// on top of launcher. Force the launcher state to rest state here.
|
||||
startState = activity.getStateManager().getRestState();
|
||||
// Do not animate the transition
|
||||
activityVisible = false;
|
||||
}
|
||||
DesktopVisibilityController controller = getDesktopVisibilityController();
|
||||
if (controller != null && controller.areFreeformTasksVisible()
|
||||
&& endTarget == LAST_TASK) {
|
||||
// When we are cancelling the transition and going back to last task, move to
|
||||
// rest state instead when desktop tasks are visible.
|
||||
// If a fullscreen task is visible, launcher goes to normal state when the
|
||||
// activity is stopped. This does not happen when freeform tasks are visible
|
||||
// on top of launcher. Force the launcher state to rest state here.
|
||||
startState = activity.getStateManager().getRestState();
|
||||
// Do not animate the transition
|
||||
activityVisible = false;
|
||||
}
|
||||
}
|
||||
activity.getStateManager().goToState(startState, activityVisible);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.quickstep
|
||||
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import com.android.launcher3.config.FeatureFlags
|
||||
import com.android.launcher3.util.Executors
|
||||
import com.android.wm.shell.shared.IHomeTransitionListener.Stub
|
||||
import com.android.wm.shell.shared.IShellTransitions
|
||||
|
||||
/** Class to track visibility state of Launcher */
|
||||
class HomeVisibilityState {
|
||||
|
||||
var isHomeVisible = true
|
||||
private set
|
||||
|
||||
private var listeners = mutableSetOf<VisibilityChangeListener>()
|
||||
|
||||
fun addListener(l: VisibilityChangeListener) = listeners.add(l)
|
||||
|
||||
fun removeListener(l: VisibilityChangeListener) = listeners.remove(l)
|
||||
|
||||
fun init(transitions: IShellTransitions?) {
|
||||
if (!FeatureFlags.enableHomeTransitionListener()) return
|
||||
try {
|
||||
transitions?.setHomeTransitionListener(
|
||||
object : Stub() {
|
||||
override fun onHomeVisibilityChanged(isVisible: Boolean) {
|
||||
Executors.MAIN_EXECUTOR.execute {
|
||||
isHomeVisible = isVisible
|
||||
listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: RemoteException) {
|
||||
Log.w(TAG, "Failed call setHomeTransitionListener", e)
|
||||
}
|
||||
}
|
||||
|
||||
interface VisibilityChangeListener {
|
||||
fun onHomeVisibilityChanged(isVisible: Boolean)
|
||||
}
|
||||
|
||||
override fun toString() = "{HomeVisibilityState isHomeVisible=$isHomeVisible}"
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "HomeVisibilityState"
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Flags;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAnimUtils;
|
||||
import com.android.launcher3.LauncherInitListener;
|
||||
@@ -206,8 +207,17 @@ public final class LauncherActivityInterface extends
|
||||
@UiThread
|
||||
private Launcher getVisibleLauncher() {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
return (launcher != null) && launcher.isStarted()
|
||||
&& (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
|
||||
if (launcher == null) {
|
||||
return null;
|
||||
}
|
||||
if (launcher.isStarted() && (isInLiveTileMode() || launcher.hasBeenResumed())) {
|
||||
return launcher;
|
||||
}
|
||||
if (Flags.useActivityOverlay()
|
||||
&& SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()) {
|
||||
return launcher;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Choreographer;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
@@ -99,7 +100,7 @@ public class LauncherBackAnimationController {
|
||||
private final int mWindowScaleMarginX;
|
||||
private float mWindowScaleEndCornerRadius;
|
||||
private float mWindowScaleStartCornerRadius;
|
||||
private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
|
||||
private final Interpolator mProgressInterpolator = Interpolators.BACK_GESTURE;
|
||||
private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
|
||||
private final PointF mInitialTouchPos = new PointF();
|
||||
|
||||
@@ -302,7 +303,7 @@ public class LauncherBackAnimationController {
|
||||
if (mScrimLayer == null) {
|
||||
addScrimLayer();
|
||||
}
|
||||
mTransaction.apply();
|
||||
applyTransaction();
|
||||
}
|
||||
|
||||
private void setLauncherTargetViewVisible(boolean isVisible) {
|
||||
@@ -342,7 +343,8 @@ public class LauncherBackAnimationController {
|
||||
return;
|
||||
}
|
||||
if (mScrimLayer.isValid()) {
|
||||
mTransaction.remove(mScrimLayer).apply();
|
||||
mTransaction.remove(mScrimLayer);
|
||||
applyTransaction();
|
||||
}
|
||||
mScrimLayer = null;
|
||||
}
|
||||
@@ -396,7 +398,11 @@ public class LauncherBackAnimationController {
|
||||
mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
|
||||
mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
|
||||
}
|
||||
applyTransaction();
|
||||
}
|
||||
|
||||
private void applyTransaction() {
|
||||
mTransaction.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
@@ -511,7 +517,7 @@ public class LauncherBackAnimationController {
|
||||
float value = (Float) animation.getAnimatedValue();
|
||||
if (mScrimLayer != null && mScrimLayer.isValid()) {
|
||||
mTransaction.setAlpha(mScrimLayer, value * mScrimAlpha);
|
||||
mTransaction.apply();
|
||||
applyTransaction();
|
||||
}
|
||||
});
|
||||
mScrimAlphaAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
@@ -270,9 +270,13 @@ public class RecentTasksList {
|
||||
|
||||
int numVisibleTasks = 0;
|
||||
for (GroupedRecentTaskInfo rawTask : rawTasks) {
|
||||
if (enableDesktopWindowingMode() && rawTask.getType() == TYPE_FREEFORM) {
|
||||
GroupTask desktopTask = createDesktopTask(rawTask);
|
||||
allTasks.add(desktopTask);
|
||||
if (rawTask.getType() == TYPE_FREEFORM) {
|
||||
// TYPE_FREEFORM tasks is only created when enableDesktopWindowingMode() is true,
|
||||
// leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
|
||||
if (enableDesktopWindowingMode()) {
|
||||
GroupTask desktopTask = createDesktopTask(rawTask);
|
||||
allTasks.add(desktopTask);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
|
||||
|
||||
import android.app.WindowConfiguration;
|
||||
@@ -68,17 +67,15 @@ public class RemoteTargetGluer {
|
||||
* running tasks
|
||||
*/
|
||||
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
|
||||
if (enableDesktopWindowingMode()) {
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
int visibleTasksCount = desktopVisibilityController.getVisibleFreeformTasksCount();
|
||||
if (visibleTasksCount > 0) {
|
||||
// Allocate +1 to account for a new task added to the desktop mode
|
||||
int numHandles = visibleTasksCount + 1;
|
||||
init(context, sizingStrategy, numHandles, true /* forDesktop */);
|
||||
return;
|
||||
}
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
int visibleTasksCount = desktopVisibilityController.getVisibleFreeformTasksCount();
|
||||
if (visibleTasksCount > 0) {
|
||||
// Allocate +1 to account for a new task added to the desktop mode
|
||||
int numHandles = visibleTasksCount + 1;
|
||||
init(context, sizingStrategy, numHandles, true /* forDesktop */);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ import androidx.annotation.WorkerThread;
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.internal.util.ScreenshotRequest;
|
||||
import com.android.internal.view.AppearanceRegion;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
@@ -82,6 +81,7 @@ import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
|
||||
import com.android.wm.shell.back.IBackAnimation;
|
||||
import com.android.wm.shell.bubbles.IBubbles;
|
||||
import com.android.wm.shell.bubbles.IBubblesListener;
|
||||
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
|
||||
import com.android.wm.shell.common.pip.IPip;
|
||||
import com.android.wm.shell.common.pip.IPipAnimationListener;
|
||||
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
|
||||
@@ -91,7 +91,6 @@ import com.android.wm.shell.draganddrop.IDragAndDrop;
|
||||
import com.android.wm.shell.onehanded.IOneHanded;
|
||||
import com.android.wm.shell.recents.IRecentTasks;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.shared.IHomeTransitionListener;
|
||||
import com.android.wm.shell.shared.IShellTransitions;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreen;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||
@@ -157,7 +156,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
private IOnBackInvokedCallback mBackToLauncherCallback;
|
||||
private IRemoteAnimationRunner mBackToLauncherRunner;
|
||||
private IDragAndDrop mDragAndDrop;
|
||||
private IHomeTransitionListener mHomeTransitionListener;
|
||||
private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
|
||||
|
||||
// Used to dedupe calls to SystemUI
|
||||
private int mLastShelfHeight;
|
||||
@@ -269,7 +268,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
setBubblesListener(mBubblesListener);
|
||||
registerSplitScreenListener(mSplitScreenListener);
|
||||
registerSplitSelectListener(mSplitSelectListener);
|
||||
setHomeTransitionListener(mHomeTransitionListener);
|
||||
mHomeVisibilityState.init(mShellTransitions);
|
||||
setStartingWindowListener(mStartingWindowListener);
|
||||
setLauncherUnlockAnimationController(
|
||||
mLauncherActivityClass, mLauncherUnlockAnimationController);
|
||||
@@ -454,6 +453,17 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
|
||||
if (mSystemUiProxy != null) {
|
||||
try {
|
||||
mSystemUiProxy.setOverrideHomeButtonLongPress(duration, slopMultiplier);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call setOverrideHomeButtonLongPress", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAccessibilityButtonClicked(int displayId) {
|
||||
if (mSystemUiProxy != null) {
|
||||
@@ -808,6 +818,18 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells SysUI to update the bubble bar location to the new location.
|
||||
* @param location new location for the bubble bar
|
||||
*/
|
||||
public void setBubbleBarLocation(BubbleBarLocation location) {
|
||||
try {
|
||||
mBubbles.setBubbleBarLocation(location);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call setBubbleBarLocation");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Splitscreen
|
||||
//
|
||||
@@ -1102,22 +1124,8 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
mRemoteTransitions.remove(remoteTransition);
|
||||
}
|
||||
|
||||
public void setHomeTransitionListener(IHomeTransitionListener listener) {
|
||||
if (!FeatureFlags.enableHomeTransitionListener()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHomeTransitionListener = listener;
|
||||
|
||||
if (mShellTransitions != null) {
|
||||
try {
|
||||
mShellTransitions.setHomeTransitionListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call setHomeTransitionListener", e);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
|
||||
}
|
||||
public HomeVisibilityState getHomeVisibilityState() {
|
||||
return mHomeVisibilityState;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1558,7 +1566,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
|
||||
pw.println("\tmOneHanded=" + mOneHanded);
|
||||
pw.println("\tmShellTransitions=" + mShellTransitions);
|
||||
pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
|
||||
pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
|
||||
pw.println("\tmStartingWindow=" + mStartingWindow);
|
||||
pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
|
||||
pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
|
||||
|
||||
+4
-2
@@ -34,6 +34,7 @@ import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.NavHandle;
|
||||
import com.android.quickstep.RecentsAnimationDeviceState;
|
||||
import com.android.quickstep.TopTaskTracker;
|
||||
import com.android.quickstep.util.AssistStateManager;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
/**
|
||||
@@ -64,8 +65,9 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer {
|
||||
super(delegate, inputMonitor);
|
||||
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
|
||||
mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get();
|
||||
if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
|
||||
mLongPressTimeout = FeatureFlags.LPNH_TIMEOUT_MS.get();
|
||||
AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(context);
|
||||
if (assistStateManager.getLPNHDurationMillis().isPresent()) {
|
||||
mLongPressTimeout = assistStateManager.getLPNHDurationMillis().get().intValue();
|
||||
} else {
|
||||
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public class AppPairsController {
|
||||
|
||||
IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
newAppPair.getContents().forEach(member -> {
|
||||
newAppPair.getAppContents().forEach(member -> {
|
||||
member.title = "";
|
||||
member.bitmap = iconCache.getDefaultIcon(newAppPair.user);
|
||||
iconCache.getTitleAndIcon(member, member.usingLowResIcon());
|
||||
|
||||
@@ -52,6 +52,16 @@ public class AssistStateManager implements ResourceBasedOverride {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Get the Launcher overridden long press duration to trigger Assistant. */
|
||||
public Optional<Long> getLPNHDurationMillis() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Get the Launcher overridden long press touch slop multiplier to trigger Assistant. */
|
||||
public Optional<Long> getLPNHCustomSlopMultiplier() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Return {@code true} if the Settings toggle is enabled. */
|
||||
public boolean isSettingsAllEntrypointsEnabled() {
|
||||
return false;
|
||||
|
||||
@@ -671,6 +671,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
|
||||
appIcon2,
|
||||
dividerPos
|
||||
)
|
||||
floatingView.bringToFront()
|
||||
|
||||
// Launcher animation: animate the floating view, expanding to fill the display surface
|
||||
progressUpdater.addUpdateListener(
|
||||
|
||||
@@ -35,7 +35,6 @@ import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_SINGLE_TASK
|
||||
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT;
|
||||
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
|
||||
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
|
||||
|
||||
@@ -70,6 +69,7 @@ import android.window.RemoteTransition;
|
||||
import android.window.TransitionInfo;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -132,6 +132,7 @@ public class SplitSelectStateController {
|
||||
private final StatsLogManager mStatsLogManager;
|
||||
private final SystemUiProxy mSystemUiProxy;
|
||||
private final StateManager mStateManager;
|
||||
@Nullable
|
||||
private SplitFromDesktopController mSplitFromDesktopController;
|
||||
@Nullable
|
||||
private DepthController mDepthController;
|
||||
@@ -208,6 +209,9 @@ public class SplitSelectStateController {
|
||||
mActivityBackCallback = null;
|
||||
mAppPairsController.onDestroy();
|
||||
mSplitSelectDataHolder.onDestroy();
|
||||
if (mSplitFromDesktopController != null) {
|
||||
mSplitFromDesktopController.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,7 +647,12 @@ public class SplitSelectStateController {
|
||||
}
|
||||
|
||||
public void initSplitFromDesktopController(Launcher launcher) {
|
||||
mSplitFromDesktopController = new SplitFromDesktopController(launcher);
|
||||
initSplitFromDesktopController(new SplitFromDesktopController(launcher));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initSplitFromDesktopController(SplitFromDesktopController controller) {
|
||||
mSplitFromDesktopController = controller;
|
||||
}
|
||||
|
||||
private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
|
||||
@@ -968,7 +977,6 @@ public class SplitSelectStateController {
|
||||
@Override
|
||||
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
|
||||
int splitPosition, Rect taskBounds) {
|
||||
if (!enableDesktopWindowingMode()) return false;
|
||||
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
|
||||
taskBounds));
|
||||
return true;
|
||||
@@ -977,6 +985,11 @@ public class SplitSelectStateController {
|
||||
SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener);
|
||||
}
|
||||
|
||||
void onDestroy() {
|
||||
SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
|
||||
mSplitSelectListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter split select from desktop mode.
|
||||
* @param taskInfo the desktop task to move to split stage
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -197,8 +196,6 @@ public class SplitToWorkspaceController {
|
||||
}
|
||||
|
||||
private boolean shouldIgnoreSecondSplitLaunch() {
|
||||
return (!FeatureFlags.enableSplitContextually()
|
||||
&& !enableDesktopWindowingMode())
|
||||
|| !mController.isSplitSelectActive();
|
||||
return !FeatureFlags.enableSplitContextually() || !mController.isSplitSelectActive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
|
||||
import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
|
||||
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
@@ -265,11 +264,11 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
|
||||
|
||||
@Override
|
||||
public void onGestureAnimationEnd() {
|
||||
DesktopVisibilityController desktopVisibilityController = null;
|
||||
DesktopVisibilityController desktopVisibilityController =
|
||||
mActivity.getDesktopVisibilityController();
|
||||
boolean showDesktopApps = false;
|
||||
GestureState.GestureEndTarget endTarget = null;
|
||||
if (enableDesktopWindowingMode()) {
|
||||
desktopVisibilityController = mActivity.getDesktopVisibilityController();
|
||||
if (desktopVisibilityController != null) {
|
||||
endTarget = mCurrentGestureEndTarget;
|
||||
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
|
||||
&& desktopVisibilityController.areFreeformTasksVisible()) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -107,6 +108,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
|
||||
private MultiValueAlpha mMultiValueAlpha;
|
||||
|
||||
protected LinearLayout mActionButtons;
|
||||
// The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is an
|
||||
// ImageButton in go launcher (does not share a common class with Button). Take care when
|
||||
// casting this.
|
||||
@@ -151,7 +153,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
|
||||
mActionButtons = findViewById(R.id.action_buttons);
|
||||
mMultiValueAlpha = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
|
||||
mMultiValueAlpha.setUpdateVisibility(true);
|
||||
|
||||
mScreenshotButton = findViewById(R.id.action_screenshot);
|
||||
@@ -243,13 +246,13 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
|
||||
/**
|
||||
* Updates a batch of flags to hide and show actions buttons for tablet/non tablet case.
|
||||
* @param isSmallScreen True if the current display is a small screen.
|
||||
*/
|
||||
public void updateForSmallScreen(boolean isSmallScreen) {
|
||||
private void updateForIsTablet() {
|
||||
assert mDp != null;
|
||||
// Update flags to see if split button should be hidden.
|
||||
updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, isSmallScreen);
|
||||
updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
|
||||
// Update flags to see if save app pair button should be hidden.
|
||||
updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, isSmallScreen);
|
||||
updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, !mDp.isTablet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +277,10 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
mScreenshotButtonHiddenFlags &= ~flag;
|
||||
}
|
||||
int desiredVisibility = mScreenshotButtonHiddenFlags == 0 ? VISIBLE : GONE;
|
||||
mScreenshotButton.setVisibility(desiredVisibility);
|
||||
if (mScreenshotButton.getVisibility() != desiredVisibility) {
|
||||
mScreenshotButton.setVisibility(desiredVisibility);
|
||||
mActionButtons.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,8 +298,10 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
mSplitButtonHiddenFlags &= ~flag;
|
||||
}
|
||||
int desiredVisibility = mSplitButtonHiddenFlags == 0 ? VISIBLE : GONE;
|
||||
mSplitButton.setVisibility(desiredVisibility);
|
||||
findViewById(R.id.action_split_space).setVisibility(desiredVisibility);
|
||||
if (mSplitButton.getVisibility() != desiredVisibility) {
|
||||
mSplitButton.setVisibility(desiredVisibility);
|
||||
mActionButtons.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,7 +323,10 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
mAppPairButtonHiddenFlags &= ~flag;
|
||||
}
|
||||
int desiredVisibility = mAppPairButtonHiddenFlags == 0 ? VISIBLE : GONE;
|
||||
mSaveAppPairButton.setVisibility(desiredVisibility);
|
||||
if (mSaveAppPairButton.getVisibility() != desiredVisibility) {
|
||||
mSaveAppPairButton.setVisibility(desiredVisibility);
|
||||
mActionButtons.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public MultiProperty getContentAlpha() {
|
||||
@@ -342,7 +353,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
* Returns the visibility of the overview actions buttons.
|
||||
*/
|
||||
public @Visibility int getActionsButtonVisibility() {
|
||||
return findViewById(R.id.action_buttons).getVisibility();
|
||||
return mActionButtons.getVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,8 +369,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
if (mDp == null) {
|
||||
return;
|
||||
}
|
||||
LayoutParams actionParams = (LayoutParams) findViewById(
|
||||
R.id.action_buttons).getLayoutParams();
|
||||
LayoutParams actionParams = (LayoutParams) mActionButtons.getLayoutParams();
|
||||
actionParams.setMargins(
|
||||
actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
|
||||
actionParams.rightMargin, getBottomMargin());
|
||||
@@ -386,6 +396,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
mDp = dp;
|
||||
mTaskSize.set(taskSize);
|
||||
updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
|
||||
updateForIsTablet();
|
||||
|
||||
requestLayout();
|
||||
|
||||
|
||||
@@ -1061,6 +1061,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
@Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
|
||||
mActionsView = actionsView;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
|
||||
// Update flags for 1p/3p launchers
|
||||
mActionsView.updateFor3pLauncher(!supportsAppPairs());
|
||||
mSplitSelectStateController = splitController;
|
||||
mDesktopRecentsTransitionController = desktopRecentsTransitionController;
|
||||
}
|
||||
@@ -3962,15 +3964,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
// Update flags to see if actions bar should show buttons for a single task or a pair of
|
||||
// tasks.
|
||||
mActionsView.updateForGroupedTask(isCurrentSplit);
|
||||
// Update flags to see if actions bar should show buttons for tablets or phones.
|
||||
mActionsView.updateForSmallScreen(!mActivity.getDeviceProfile().isTablet);
|
||||
// Update flags for 1p/3p launchers
|
||||
mActionsView.updateFor3pLauncher(!supportsAppPairs());
|
||||
|
||||
if (enableDesktopWindowingMode()) {
|
||||
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
|
||||
}
|
||||
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
|
||||
}
|
||||
|
||||
/** Returns if app pairs are supported in this launcher. Overridden in subclasses. */
|
||||
@@ -4692,9 +4688,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
|
||||
true /*animateCurrentTaskDismissal*/);
|
||||
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
|
||||
if (enableDesktopWindowingMode()) {
|
||||
updateDesktopTaskVisibility(false /* visible */);
|
||||
}
|
||||
updateDesktopTaskVisibility(false /* visible */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4716,9 +4710,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
|
||||
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
|
||||
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
|
||||
if (enableDesktopWindowingMode()) {
|
||||
updateDesktopTaskVisibility(false /* visible */);
|
||||
}
|
||||
updateDesktopTaskVisibility(false /* visible */);
|
||||
}
|
||||
|
||||
private void updateDesktopTaskVisibility(boolean visible) {
|
||||
@@ -4922,9 +4914,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
|
||||
mSplitHiddenTaskView = null;
|
||||
}
|
||||
if (enableDesktopWindowingMode()) {
|
||||
updateDesktopTaskVisibility(true /* visible */);
|
||||
}
|
||||
updateDesktopTaskVisibility(true /* visible */);
|
||||
}
|
||||
|
||||
private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
|
||||
|
||||
+14
@@ -36,6 +36,7 @@ import com.android.launcher3.util.ComponentKey
|
||||
import com.android.launcher3.util.SplitConfigurationOptions
|
||||
import com.android.quickstep.RecentsModel
|
||||
import com.android.quickstep.SystemUiProxy
|
||||
import com.android.quickstep.util.SplitSelectStateController.SplitFromDesktopController
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
|
||||
import java.util.function.Consumer
|
||||
@@ -65,6 +66,7 @@ class SplitSelectStateControllerTest {
|
||||
private val context: StatefulActivity<*> = mock()
|
||||
private val recentsModel: RecentsModel = mock()
|
||||
private val pendingIntent: PendingIntent = mock()
|
||||
private val splitFromDesktopController: SplitFromDesktopController = mock()
|
||||
|
||||
private lateinit var splitSelectStateController: SplitSelectStateController
|
||||
|
||||
@@ -607,6 +609,18 @@ class SplitSelectStateControllerTest {
|
||||
assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitSelectStateControllerDestroyed_SplitFromDesktopControllerAlsoDestroyed() {
|
||||
// Initiate split from desktop controller
|
||||
splitSelectStateController.initSplitFromDesktopController(splitFromDesktopController)
|
||||
|
||||
// Simulate default controller being destroyed
|
||||
splitSelectStateController.onDestroy()
|
||||
|
||||
// Verify desktop controller is also destroyed
|
||||
verify(splitFromDesktopController).onDestroy()
|
||||
}
|
||||
|
||||
// Generate GroupTask with default userId.
|
||||
private fun generateGroupTask(
|
||||
task1ComponentName: ComponentName,
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.model
|
||||
|
||||
import android.app.prediction.AppTarget
|
||||
import android.app.prediction.AppTargetEvent
|
||||
import android.app.prediction.AppTargetId
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Process.myUserHandle
|
||||
import android.os.UserHandle
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.launcher3.DeviceProfile
|
||||
import com.android.launcher3.InvariantDeviceProfile
|
||||
import com.android.launcher3.LauncherAppState
|
||||
import com.android.launcher3.icons.IconCache
|
||||
import com.android.launcher3.model.WidgetPredictionsRequester.buildBundleForPredictionSession
|
||||
import com.android.launcher3.model.WidgetPredictionsRequester.filterPredictions
|
||||
import com.android.launcher3.model.WidgetPredictionsRequester.notOnUiSurfaceFilter
|
||||
import com.android.launcher3.util.ActivityContextWrapper
|
||||
import com.android.launcher3.util.PackageUserKey
|
||||
import com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.function.Predicate
|
||||
import junit.framework.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class WidgetsPredictionsRequesterTest {
|
||||
|
||||
private lateinit var mUserHandle: UserHandle
|
||||
private lateinit var context: Context
|
||||
private lateinit var deviceProfile: DeviceProfile
|
||||
private lateinit var testInvariantProfile: InvariantDeviceProfile
|
||||
|
||||
private lateinit var widget1aInfo: AppWidgetProviderInfo
|
||||
private lateinit var widget1bInfo: AppWidgetProviderInfo
|
||||
private lateinit var widget2Info: AppWidgetProviderInfo
|
||||
|
||||
private lateinit var widgetItem1a: WidgetItem
|
||||
private lateinit var widgetItem1b: WidgetItem
|
||||
private lateinit var widgetItem2: WidgetItem
|
||||
|
||||
private lateinit var allWidgets: Map<PackageUserKey, List<WidgetItem>>
|
||||
|
||||
@Mock private lateinit var iconCache: IconCache
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
mUserHandle = myUserHandle()
|
||||
context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
|
||||
testInvariantProfile = LauncherAppState.getIDP(context)
|
||||
deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
|
||||
|
||||
widget1aInfo =
|
||||
createAppWidgetProviderInfo(
|
||||
ComponentName.createRelative(APP_1_PACKAGE_NAME, APP_1_PROVIDER_A_CLASS_NAME)
|
||||
)
|
||||
widget1bInfo =
|
||||
createAppWidgetProviderInfo(
|
||||
ComponentName.createRelative(APP_1_PACKAGE_NAME, APP_1_PROVIDER_B_CLASS_NAME)
|
||||
)
|
||||
widgetItem1a = createWidgetItem(widget1aInfo)
|
||||
widgetItem1b = createWidgetItem(widget1bInfo)
|
||||
|
||||
widget2Info =
|
||||
createAppWidgetProviderInfo(
|
||||
ComponentName.createRelative(APP_2_PACKAGE_NAME, APP_2_PROVIDER_1_CLASS_NAME)
|
||||
)
|
||||
widgetItem2 = createWidgetItem(widget2Info)
|
||||
|
||||
allWidgets =
|
||||
mapOf(
|
||||
PackageUserKey(APP_1_PACKAGE_NAME, mUserHandle) to
|
||||
listOf(widgetItem1a, widgetItem1b),
|
||||
PackageUserKey(APP_2_PACKAGE_NAME, mUserHandle) to listOf(widgetItem2),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildBundleForPredictionSession_includesAddedAppWidgets() {
|
||||
val existingWidgets = arrayListOf(widget1aInfo, widget1bInfo, widget2Info)
|
||||
|
||||
val bundle = buildBundleForPredictionSession(existingWidgets, TEST_UI_SURFACE)
|
||||
val addedWidgetsBundleExtra =
|
||||
bundle.getParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, AppTarget::class.java)
|
||||
|
||||
assertNotNull(addedWidgetsBundleExtra)
|
||||
assertThat(addedWidgetsBundleExtra)
|
||||
.containsExactly(
|
||||
buildExpectedAppTargetEvent(
|
||||
/*pkg=*/ APP_1_PACKAGE_NAME,
|
||||
/*providerClassName=*/ APP_1_PROVIDER_A_CLASS_NAME,
|
||||
/*user=*/ mUserHandle
|
||||
),
|
||||
buildExpectedAppTargetEvent(
|
||||
/*pkg=*/ APP_1_PACKAGE_NAME,
|
||||
/*providerClassName=*/ APP_1_PROVIDER_B_CLASS_NAME,
|
||||
/*user=*/ mUserHandle
|
||||
),
|
||||
buildExpectedAppTargetEvent(
|
||||
/*pkg=*/ APP_2_PACKAGE_NAME,
|
||||
/*providerClassName=*/ APP_2_PROVIDER_1_CLASS_NAME,
|
||||
/*user=*/ mUserHandle
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filterPredictions_notOnUiSurfaceFilter_returnsOnlyEligiblePredictions() {
|
||||
val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
|
||||
val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
|
||||
|
||||
val predictions =
|
||||
listOf(
|
||||
// already on surface
|
||||
AppTarget(
|
||||
AppTargetId(APP_1_PACKAGE_NAME),
|
||||
APP_1_PACKAGE_NAME,
|
||||
APP_1_PROVIDER_B_CLASS_NAME,
|
||||
mUserHandle
|
||||
),
|
||||
// eligible
|
||||
AppTarget(
|
||||
AppTargetId(APP_2_PACKAGE_NAME),
|
||||
APP_2_PACKAGE_NAME,
|
||||
APP_2_PROVIDER_1_CLASS_NAME,
|
||||
mUserHandle
|
||||
)
|
||||
)
|
||||
|
||||
// only 2 was eligible
|
||||
assertThat(filterPredictions(predictions, allWidgets, filter)).containsExactly(widgetItem2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filterPredictions_appPredictions_returnsWidgetFromPackage() {
|
||||
val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
|
||||
val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
|
||||
|
||||
val predictions =
|
||||
listOf(
|
||||
AppTarget(
|
||||
AppTargetId(APP_1_PACKAGE_NAME),
|
||||
APP_1_PACKAGE_NAME,
|
||||
"$APP_1_PACKAGE_NAME.SomeActivity",
|
||||
mUserHandle
|
||||
),
|
||||
AppTarget(
|
||||
AppTargetId(APP_2_PACKAGE_NAME),
|
||||
APP_2_PACKAGE_NAME,
|
||||
"$APP_2_PACKAGE_NAME.SomeActivity2",
|
||||
mUserHandle
|
||||
),
|
||||
)
|
||||
|
||||
assertThat(filterPredictions(predictions, allWidgets, filter))
|
||||
.containsExactly(widgetItem1a, widgetItem2)
|
||||
}
|
||||
|
||||
private fun createWidgetItem(
|
||||
providerInfo: AppWidgetProviderInfo,
|
||||
): WidgetItem {
|
||||
val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
|
||||
return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEST_UI_SURFACE = "widgets_test"
|
||||
const val BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"
|
||||
|
||||
const val APP_1_PACKAGE_NAME = "com.example.app1"
|
||||
const val APP_1_PROVIDER_A_CLASS_NAME = "app1Provider1"
|
||||
const val APP_1_PROVIDER_B_CLASS_NAME = "app1Provider2"
|
||||
|
||||
const val APP_2_PACKAGE_NAME = "com.example.app2"
|
||||
const val APP_2_PROVIDER_1_CLASS_NAME = "app2Provider1"
|
||||
|
||||
const val TEST_PACKAGE = "pkg"
|
||||
|
||||
private fun buildExpectedAppTargetEvent(
|
||||
pkg: String,
|
||||
providerClassName: String,
|
||||
userHandle: UserHandle
|
||||
): AppTargetEvent {
|
||||
val appTarget =
|
||||
AppTarget.Builder(
|
||||
/*id=*/ AppTargetId("widget:$pkg"),
|
||||
/*packageName=*/ pkg,
|
||||
/*user=*/ userHandle
|
||||
)
|
||||
.setClassName(providerClassName)
|
||||
.build()
|
||||
return AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_PIN)
|
||||
.setLaunchLocation(TEST_UI_SURFACE)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
|
||||
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
|
||||
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
|
||||
|
||||
import android.graphics.Rect;
|
||||
@@ -23,6 +25,7 @@ import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
|
||||
import com.android.launcher3.util.rule.TestStabilityRule;
|
||||
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
|
||||
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
|
||||
|
||||
@@ -45,6 +48,7 @@ public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar {
|
||||
|
||||
@Test
|
||||
@NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
|
||||
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
|
||||
public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
|
||||
Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
|
||||
// Go home and to an IME activity (any configuration change would do, as long as it
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2024 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.
|
||||
-->
|
||||
|
||||
<com.android.launcher3.apppairs.AppPairIcon
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:focusable="true"
|
||||
launcher:iconDisplay="folder" >
|
||||
<com.android.launcher3.apppairs.AppPairIconGraphic
|
||||
android:id="@+id/app_pair_icon_graphic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="false" />
|
||||
<com.android.launcher3.BubbleTextView
|
||||
style="@style/BaseIcon"
|
||||
android:id="@+id/app_pair_icon_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="false"
|
||||
android:layout_gravity="top"
|
||||
android:textColor="?attr/folderTextColor"
|
||||
launcher:iconDisplay="folder" />
|
||||
</com.android.launcher3.apppairs.AppPairIcon>
|
||||
@@ -35,14 +35,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_gravity="fill"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_badge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_margin="@dimen/profile_badge_margin"/>
|
||||
</com.android.launcher3.widget.WidgetCellPreview>
|
||||
|
||||
<FrameLayout
|
||||
|
||||
@@ -277,7 +277,6 @@
|
||||
|
||||
<!-- Sizes for managed profile badges -->
|
||||
<dimen name="profile_badge_size">24dp</dimen>
|
||||
<dimen name="profile_badge_margin">5dp</dimen>
|
||||
<dimen name="profile_badge_minimum_top">2dp</dimen>
|
||||
|
||||
<!-- Shadows and outlines -->
|
||||
|
||||
@@ -680,7 +680,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
mStateManager.getState().onBackCancelled(Launcher.this);
|
||||
Launcher.this.onBackCancelled();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2086,6 +2086,10 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
mStateManager.getState().onBackInvoked(this);
|
||||
}
|
||||
|
||||
protected void onBackCancelled() {
|
||||
mStateManager.getState().onBackCancelled(this);
|
||||
}
|
||||
|
||||
protected void onScreenOnChanged(boolean isOn) {
|
||||
// Reset AllApps to its initial state only if we are not in the middle of
|
||||
// processing a multi-step drop
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.EDIT_MODE;
|
||||
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
|
||||
@@ -1873,12 +1873,9 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo
|
||||
&& ((WorkspaceItemInfo) dropOverView.getTag()).container
|
||||
!= LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
|
||||
boolean willBecomeShortcut =
|
||||
(info.itemType == ITEM_TYPE_APPLICATION ||
|
||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
|
||||
boolean aboveShortcut = Folder.willAccept(dropOverView.getTag())
|
||||
&& ((ItemInfo) dropOverView.getTag()).container != CONTAINER_HOTSEAT_PREDICTION;
|
||||
boolean willBecomeShortcut = Folder.willAcceptItemType(info.itemType);
|
||||
|
||||
return (aboveShortcut && willBecomeShortcut);
|
||||
}
|
||||
@@ -1925,12 +1922,12 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
mCreateUserFolderOnDrop = false;
|
||||
final int screenId = getCellLayoutId(target);
|
||||
|
||||
boolean aboveShortcut = (v.getTag() instanceof WorkspaceItemInfo);
|
||||
boolean willBecomeShortcut = (newView.getTag() instanceof WorkspaceItemInfo);
|
||||
boolean aboveShortcut = Folder.willAccept(v.getTag());
|
||||
boolean willBecomeShortcut = Folder.willAccept(newView.getTag());
|
||||
|
||||
if (aboveShortcut && willBecomeShortcut) {
|
||||
WorkspaceItemInfo sourceInfo = (WorkspaceItemInfo) newView.getTag();
|
||||
WorkspaceItemInfo destInfo = (WorkspaceItemInfo) v.getTag();
|
||||
ItemInfo sourceInfo = (ItemInfo) newView.getTag();
|
||||
ItemInfo destInfo = (ItemInfo) v.getTag();
|
||||
// if the drag started here, we need to remove it from the workspace
|
||||
if (!external) {
|
||||
getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
|
||||
@@ -3314,7 +3311,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
}
|
||||
} else if (child instanceof FolderIcon) {
|
||||
FolderInfo folderInfo = (FolderInfo) info;
|
||||
List<WorkspaceItemInfo> matches = folderInfo.getContents().stream()
|
||||
List<ItemInfo> matches = folderInfo.getContents().stream()
|
||||
.filter(matcher)
|
||||
.collect(Collectors.toList());
|
||||
if (!matches.isEmpty()) {
|
||||
@@ -3381,7 +3378,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
FolderInfo fi = (FolderInfo) info;
|
||||
if (fi.anyMatch(matcher)) {
|
||||
FolderDotInfo folderDotInfo = new FolderDotInfo();
|
||||
for (WorkspaceItemInfo si : fi.getContents()) {
|
||||
for (ItemInfo si : fi.getContents()) {
|
||||
folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
|
||||
}
|
||||
((FolderIcon) v).setDotInfo(folderDotInfo);
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.View;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
@@ -117,7 +118,7 @@ public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelega
|
||||
return mContext.getString(R.string.item_moved);
|
||||
} else {
|
||||
ItemInfo info = (ItemInfo) child.getTag();
|
||||
if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
|
||||
if (Folder.willAccept(info)) {
|
||||
return mContext.getString(R.string.folder_created);
|
||||
|
||||
} else if (info instanceof FolderInfo) {
|
||||
@@ -148,8 +149,8 @@ public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelega
|
||||
if (TextUtils.isEmpty(info.title)) {
|
||||
// Find the first item in the folder.
|
||||
FolderInfo folder = (FolderInfo) info;
|
||||
WorkspaceItemInfo firstItem = null;
|
||||
for (WorkspaceItemInfo shortcut : folder.getContents()) {
|
||||
ItemInfo firstItem = null;
|
||||
for (ItemInfo shortcut : folder.getContents()) {
|
||||
if (firstItem == null || firstItem.rank > shortcut.rank) {
|
||||
firstItem = shortcut;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
@@ -1366,6 +1367,18 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
|
||||
invalidateHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link Animator.AnimatorListener} on {@link mAllAppsTransitionController} to observe
|
||||
* animation of backing out of all apps search view to all apps view.
|
||||
*/
|
||||
public void setAllAppsSearchBackAnimatorListener(Animator.AnimatorListener listener) {
|
||||
Preconditions.assertNotNull(mAllAppsTransitionController);
|
||||
if (mAllAppsTransitionController == null) {
|
||||
return;
|
||||
}
|
||||
mAllAppsTransitionController.setAllAppsSearchBackAnimationListener(listener);
|
||||
}
|
||||
|
||||
public void setScrimView(ScrimView scrimView) {
|
||||
mScrimView = scrimView;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.app.animation.Interpolators;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -167,6 +168,8 @@ public class AllAppsTransitionController
|
||||
private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
|
||||
private final int mNavScrimFlag;
|
||||
|
||||
@Nullable private Animator.AnimatorListener mAllAppsSearchBackAnimationListener;
|
||||
|
||||
private boolean mIsVerticalLayout;
|
||||
|
||||
// Animation in this class is controlled by a single variable {@link mProgress}.
|
||||
@@ -312,11 +315,25 @@ public class AllAppsTransitionController
|
||||
}
|
||||
}
|
||||
|
||||
/** Animate all apps view to 1f scale. */
|
||||
/** Set {@link Animator.AnimatorListener} for scaling all apps scale to 1 animation. */
|
||||
public void setAllAppsSearchBackAnimationListener(Animator.AnimatorListener listener) {
|
||||
mAllAppsSearchBackAnimationListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate all apps view to 1f scale. This is called when backing (exiting) from all apps
|
||||
* search view to all apps view.
|
||||
*/
|
||||
public void animateAllAppsToNoScale() {
|
||||
mAllAppScale.animateToValue(1f)
|
||||
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
|
||||
.start();
|
||||
if (mAllAppScale.isAnimating()) {
|
||||
return;
|
||||
}
|
||||
Animator animator = mAllAppScale.animateToValue(1f)
|
||||
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS);
|
||||
if (mAllAppsSearchBackAnimationListener != null) {
|
||||
animator.addListener(mAllAppsSearchBackAnimationListener);
|
||||
}
|
||||
animator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,11 +30,13 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Reorderable;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.util.MultiTranslateDelegate;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
@@ -179,10 +181,18 @@ public class AppPairIcon extends FrameLayout implements DraggableView, Reorderab
|
||||
return mIconGraphic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that both app icons in the pair are loaded in high resolution.
|
||||
*/
|
||||
public void verifyHighRes() {
|
||||
IconCache iconCache = LauncherAppState.getInstance(getContext()).getIconCache();
|
||||
getInfo().fetchHiResIconsIfNeeded(iconCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn.
|
||||
*/
|
||||
public void maybeRedrawForWorkspaceUpdate(Predicate<WorkspaceItemInfo> itemCheck) {
|
||||
public void maybeRedrawForWorkspaceUpdate(Predicate<ItemInfo> itemCheck) {
|
||||
// If either of the app pair icons return true on the predicate (i.e. in the list of
|
||||
// updated apps), redraw the icon graphic (icon background and both icons).
|
||||
if (getInfo().anyMatch(itemCheck)) {
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.android.launcher3.icons.FastBitmapDrawable;
|
||||
* A composed Drawable consisting of the two app pair icons and the background behind them (looks
|
||||
* like two rectangles).
|
||||
*/
|
||||
class AppPairIconDrawable extends Drawable {
|
||||
public class AppPairIconDrawable extends Drawable {
|
||||
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final AppPairIconDrawingParams mP;
|
||||
private final FastBitmapDrawable mIcon1;
|
||||
@@ -102,6 +102,7 @@ class AppPairIconDrawable extends Drawable {
|
||||
}
|
||||
|
||||
mIcon2.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,4 +206,14 @@ class AppPairIconDrawable extends Drawable {
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mBackgroundPaint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return mP.getIconSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return mP.getIconSize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,22 +77,29 @@ class AppPairIconDrawingParams(val context: Context, container: Int) {
|
||||
innerPadding = iconSize * INNER_PADDING_SCALE
|
||||
memberIconSize = iconSize * MEMBER_ICON_SCALE
|
||||
updateOrientation(dp)
|
||||
if (container == DISPLAY_FOLDER) {
|
||||
val ta =
|
||||
context.theme.obtainStyledAttributes(
|
||||
intArrayOf(R.attr.materialColorSurfaceContainerLowest)
|
||||
)
|
||||
bgColor = ta.getColor(0, 0)
|
||||
ta.recycle()
|
||||
} else {
|
||||
val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
|
||||
bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
|
||||
ta.recycle()
|
||||
}
|
||||
bgColor = getBgColorForContainer(container)
|
||||
}
|
||||
|
||||
/** Checks the device orientation and updates isLeftRightSplit accordingly. */
|
||||
fun updateOrientation(dp: DeviceProfile) {
|
||||
isLeftRightSplit = dp.isLeftRightSplit
|
||||
}
|
||||
|
||||
private fun getBgColorForContainer(container: Int): Int {
|
||||
val color: Int
|
||||
if (container == DISPLAY_FOLDER) {
|
||||
val ta =
|
||||
context.theme.obtainStyledAttributes(
|
||||
intArrayOf(R.attr.materialColorSurfaceContainerLowest)
|
||||
)
|
||||
color = ta.getColor(0, 0)
|
||||
ta.recycle()
|
||||
} else {
|
||||
val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
|
||||
color = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
|
||||
ta.recycle()
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.launcher3.apppairs
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.widget.FrameLayout
|
||||
@@ -52,7 +51,10 @@ constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
* 2) One of the member apps can't be launched due to screen size requirements.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun composeDrawable(appPairInfo: AppPairInfo, p: AppPairIconDrawingParams): Drawable {
|
||||
fun composeDrawable(
|
||||
appPairInfo: AppPairInfo,
|
||||
p: AppPairIconDrawingParams
|
||||
): AppPairIconDrawable {
|
||||
// Generate new icons, using themed flag if needed.
|
||||
val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0
|
||||
val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, flags)
|
||||
@@ -81,7 +83,7 @@ constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
|
||||
private lateinit var parentIcon: AppPairIcon
|
||||
private lateinit var drawParams: AppPairIconDrawingParams
|
||||
private lateinit var drawable: Drawable
|
||||
lateinit var drawable: AppPairIconDrawable
|
||||
|
||||
fun init(icon: AppPairIcon, container: Int) {
|
||||
parentIcon = icon
|
||||
@@ -116,12 +118,6 @@ constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
redraw()
|
||||
}
|
||||
|
||||
/** Updates the icon drawable and redraws it */
|
||||
fun redraw() {
|
||||
drawable = composeDrawable(parentIcon.info, drawParams)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system.
|
||||
*/
|
||||
@@ -136,6 +132,12 @@ constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
)
|
||||
}
|
||||
|
||||
/** Updates the icon drawable and redraws it */
|
||||
fun redraw() {
|
||||
drawable = composeDrawable(parentIcon.info, drawParams)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun dispatchDraw(canvas: Canvas) {
|
||||
super.dispatchDraw(canvas)
|
||||
drawable.draw(canvas)
|
||||
|
||||
@@ -132,7 +132,16 @@ public final class FeatureFlags {
|
||||
|
||||
public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS =
|
||||
getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", ENABLED,
|
||||
"Add dev options to customize the LPNH trigger slop and milliseconds");
|
||||
"Add dev options and server side control to customize the LPNH "
|
||||
+ "trigger slop and milliseconds");
|
||||
|
||||
public static final BooleanFlag CUSTOM_LPH_THRESHOLDS = getReleaseFlag(331800576,
|
||||
"CUSTOM_LPH_THRESHOLDS", DISABLED,
|
||||
"Server side control to customize LPH timeout and touch slop");
|
||||
|
||||
public static final BooleanFlag OVERRIDE_LPNH_LPH_THRESHOLDS = getReleaseFlag(331799727,
|
||||
"OVERRIDE_LPNH_LPH_THRESHOLDS", DISABLED,
|
||||
"Enable AGSA override for LPNH and LPH timeout and touch slop");
|
||||
|
||||
public static final BooleanFlag ANIMATE_LPNH =
|
||||
getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD,
|
||||
|
||||
@@ -19,6 +19,9 @@ package com.android.launcher3.folder;
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
import static com.android.launcher3.LauncherState.EDIT_MODE;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
||||
@@ -66,14 +69,12 @@ import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Alarm;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.ExtendedEditText;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.OnAlarmListener;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
@@ -166,6 +167,22 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
private static final Rect sTempRect = new Rect();
|
||||
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
|
||||
|
||||
/**
|
||||
* Checks if {@code o} is an {@link ItemInfo} type that can be placed in folders.
|
||||
*/
|
||||
public static boolean willAccept(Object o) {
|
||||
return o instanceof ItemInfo info && willAcceptItemType(info.itemType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code itemType} is a type that can be placed in folders.
|
||||
*/
|
||||
public static boolean willAcceptItemType(int itemType) {
|
||||
return itemType == ITEM_TYPE_APPLICATION
|
||||
|| itemType == ITEM_TYPE_DEEP_SHORTCUT
|
||||
|| itemType == ITEM_TYPE_APP_PAIR;
|
||||
}
|
||||
|
||||
private final Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
|
||||
private final Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
|
||||
private final Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
|
||||
@@ -313,9 +330,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
|
||||
public boolean startDrag(View v, DragOptions options) {
|
||||
Object tag = v.getTag();
|
||||
if (tag instanceof WorkspaceItemInfo) {
|
||||
WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
|
||||
|
||||
if (tag instanceof ItemInfo item) {
|
||||
mEmptyCellRank = item.rank;
|
||||
mCurrentDragView = v;
|
||||
|
||||
@@ -346,14 +361,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
mContent.removeItem(mCurrentDragView);
|
||||
if (dragObject.dragInfo instanceof WorkspaceItemInfo) {
|
||||
mItemsInvalidated = true;
|
||||
mItemsInvalidated = true;
|
||||
|
||||
// We do not want to get events for the item being removed, as they will get handled
|
||||
// when the drop completes
|
||||
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
|
||||
mInfo.remove((WorkspaceItemInfo) dragObject.dragInfo, true);
|
||||
}
|
||||
// We do not want to get events for the item being removed, as they will get handled
|
||||
// when the drop completes
|
||||
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
|
||||
mInfo.remove(dragObject.dragInfo, true);
|
||||
}
|
||||
mDragInProgress = true;
|
||||
mItemAddedBackToSelfViaIcon = false;
|
||||
@@ -493,7 +506,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
mInfo = info;
|
||||
mFromTitle = info.title;
|
||||
mFromLabelState = info.getFromLabelState();
|
||||
ArrayList<WorkspaceItemInfo> children = info.getContents();
|
||||
ArrayList<ItemInfo> children = info.getContents();
|
||||
Collections.sort(children, ITEM_POS_COMPARATOR);
|
||||
updateItemLocationsInDatabaseBatch(true);
|
||||
|
||||
@@ -626,7 +639,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
// onDropComplete. Perform cleanup once drag-n-drop ends.
|
||||
mDragController.addDragListener(this);
|
||||
|
||||
ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.getContents());
|
||||
ArrayList<ItemInfo> items = new ArrayList<>(mInfo.getContents());
|
||||
mEmptyCellRank = items.size();
|
||||
items.add(null); // Add an empty spot at the end
|
||||
|
||||
@@ -647,7 +660,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
* is animated relative to the specified View. If the View is null, no animation
|
||||
* is played.
|
||||
*/
|
||||
private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
|
||||
private void animateOpen(List<ItemInfo> items, int pageNo) {
|
||||
if (items == null || items.size() <= 1) {
|
||||
Log.d(TAG, "Couldn't animate folder open because items is: " + items);
|
||||
return;
|
||||
@@ -896,8 +909,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
public boolean acceptDrop(DragObject d) {
|
||||
final ItemInfo item = d.dragInfo;
|
||||
final int itemType = item.itemType;
|
||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
|
||||
return Folder.willAcceptItemType(itemType);
|
||||
}
|
||||
|
||||
public void onDragEnter(DragObject d) {
|
||||
@@ -1050,7 +1062,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
} else {
|
||||
// The drag failed, we need to return the item to the folder
|
||||
WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
|
||||
ItemInfo info = d.dragInfo;
|
||||
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
|
||||
? mCurrentDragView : mContent.createNewView(info);
|
||||
ArrayList<View> views = getIconsInReadingOrder();
|
||||
@@ -1099,7 +1111,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
ArrayList<ItemInfo> items = new ArrayList<>();
|
||||
int total = mInfo.getContents().size();
|
||||
for (int i = 0; i < total; i++) {
|
||||
WorkspaceItemInfo itemInfo = mInfo.getContents().get(i);
|
||||
ItemInfo itemInfo = mInfo.getContents().get(i);
|
||||
if (verifier.updateRankAndPos(itemInfo, i)) {
|
||||
items.add(itemInfo);
|
||||
}
|
||||
@@ -1112,8 +1124,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
Executors.MODEL_EXECUTOR.post(() -> {
|
||||
FolderNameInfos nameInfos = new FolderNameInfos();
|
||||
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
|
||||
fnp.getSuggestedFolderName(
|
||||
getContext(), mInfo.getContents(), nameInfos);
|
||||
fnp.getSuggestedFolderName(getContext(), mInfo.getAppContents(), nameInfos);
|
||||
mInfo.suggestedFolderNames = nameInfos;
|
||||
});
|
||||
}
|
||||
@@ -1298,15 +1309,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
d.deferDragViewCleanupPostAnimation = false;
|
||||
mRearrangeOnClose = true;
|
||||
} else {
|
||||
final WorkspaceItemInfo si;
|
||||
final ItemInfo si;
|
||||
if (pasiSi != null) {
|
||||
si = pasiSi;
|
||||
} else if (d.dragInfo instanceof WorkspaceItemFactory) {
|
||||
// Came from all apps -- make a copy.
|
||||
si = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(launcher);
|
||||
} else {
|
||||
// WorkspaceItemInfo
|
||||
si = (WorkspaceItemInfo) d.dragInfo;
|
||||
// WorkspaceItemInfo or AppPairInfo
|
||||
si = d.dragInfo;
|
||||
}
|
||||
|
||||
View currentDragView;
|
||||
@@ -1314,7 +1325,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
|
||||
|
||||
// Actually move the item in the database if it was an external drag. Call this
|
||||
// before creating the view, so that WorkspaceItemInfo is updated appropriately.
|
||||
// before creating the view, so that the ItemInfo is updated appropriately.
|
||||
mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(
|
||||
si, mInfo.id, 0, si.cellX, si.cellY);
|
||||
mIsExternalDrag = false;
|
||||
@@ -1376,14 +1387,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
// This is used so the item doesn't immediately appear in the folder when added. In one case
|
||||
// we need to create the illusion that the item isn't added back to the folder yet, to
|
||||
// to correspond to the animation of the icon back into the folder. This is
|
||||
public void hideItem(WorkspaceItemInfo info) {
|
||||
public void hideItem(ItemInfo info) {
|
||||
View v = getViewForInfo(info);
|
||||
if (v != null) {
|
||||
v.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void showItem(WorkspaceItemInfo info) {
|
||||
public void showItem(ItemInfo info) {
|
||||
View v = getViewForInfo(info);
|
||||
if (v != null) {
|
||||
v.setVisibility(VISIBLE);
|
||||
@@ -1391,7 +1402,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdd(WorkspaceItemInfo item, int rank) {
|
||||
public void onAdd(ItemInfo item, int rank) {
|
||||
FolderGridOrganizer verifier = new FolderGridOrganizer(
|
||||
mActivityContext.getDeviceProfile()).setFolderInfo(mInfo);
|
||||
verifier.updateRankAndPos(item, rank);
|
||||
@@ -1406,7 +1417,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(List<WorkspaceItemInfo> items) {
|
||||
public void onRemove(List<ItemInfo> items) {
|
||||
mItemsInvalidated = true;
|
||||
items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
|
||||
if (mState == STATE_ANIMATING) {
|
||||
@@ -1423,7 +1434,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
}
|
||||
|
||||
private View getViewForInfo(final WorkspaceItemInfo item) {
|
||||
private View getViewForInfo(final ItemInfo item) {
|
||||
return mContent.iterateOverItems((info, view) -> info == item);
|
||||
}
|
||||
|
||||
@@ -1432,6 +1443,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
updateTextViewFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleChanged(CharSequence title) {
|
||||
mFolderName.setText(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility methods to iterate over items of the view
|
||||
*/
|
||||
@@ -1451,7 +1467,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
return mItemsInReadingOrder;
|
||||
}
|
||||
|
||||
public List<BubbleTextView> getItemsOnPage(int page) {
|
||||
public List<View> getItemsOnPage(int page) {
|
||||
ArrayList<View> allItems = getIconsInReadingOrder();
|
||||
int lastPage = mContent.getPageCount() - 1;
|
||||
int totalItemsInFolder = allItems.size();
|
||||
@@ -1463,9 +1479,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
int startIndex = page * itemsPerPage;
|
||||
int endIndex = Math.min(startIndex + numItemsOnCurrentPage, allItems.size());
|
||||
|
||||
List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
|
||||
List<View> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
|
||||
for (int i = startIndex; i < endIndex; ++i) {
|
||||
itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
|
||||
itemsOnCurrentPage.add(allItems.get(i));
|
||||
}
|
||||
return itemsOnCurrentPage;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.PropertyResetListener;
|
||||
import com.android.launcher3.apppairs.AppPairIcon;
|
||||
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
@@ -127,7 +128,7 @@ public class FolderAnimationManager {
|
||||
(BaseDragLayer.LayoutParams) mFolder.getLayoutParams();
|
||||
mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
|
||||
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
|
||||
final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
|
||||
final List<View> itemsInPreview = getPreviewIconsOnPage(0);
|
||||
|
||||
// Match position of the FolderIcon
|
||||
final Rect folderIconPos = new Rect();
|
||||
@@ -139,8 +140,8 @@ public class FolderAnimationManager {
|
||||
// Match size/scale of icons in the preview
|
||||
float previewScale = rule.scaleForItem(itemsInPreview.size());
|
||||
float previewSize = rule.getIconSize() * previewScale;
|
||||
float initialScale = previewSize / itemsInPreview.get(0).getIconSize()
|
||||
* scaleRelativeToDragLayer;
|
||||
float baseIconSize = getBubbleTextView(itemsInPreview.get(0)).getIconSize();
|
||||
float initialScale = previewSize / baseIconSize * scaleRelativeToDragLayer;
|
||||
final float finalScale = 1f;
|
||||
float scale = mIsOpening ? initialScale : finalScale;
|
||||
mFolder.setPivotX(0);
|
||||
@@ -198,11 +199,12 @@ public class FolderAnimationManager {
|
||||
// Initialize the Folder items' text.
|
||||
PropertyResetListener colorResetListener =
|
||||
new PropertyResetListener<>(TEXT_ALPHA_PROPERTY, 1f);
|
||||
for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
|
||||
for (View icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
|
||||
BubbleTextView titleText = getBubbleTextView(icon);
|
||||
if (mIsOpening) {
|
||||
icon.setTextVisibility(false);
|
||||
titleText.setTextVisibility(false);
|
||||
}
|
||||
ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening);
|
||||
ObjectAnimator anim = titleText.createTextAlphaAnimator(mIsOpening);
|
||||
anim.addListener(colorResetListener);
|
||||
play(a, anim);
|
||||
}
|
||||
@@ -339,7 +341,7 @@ public class FolderAnimationManager {
|
||||
/**
|
||||
* Returns the list of "preview items" on {@param page}.
|
||||
*/
|
||||
private List<BubbleTextView> getPreviewIconsOnPage(int page) {
|
||||
private List<View> getPreviewIconsOnPage(int page) {
|
||||
return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
|
||||
.previewItemsForPage(page, mFolder.getIconsInReadingOrder());
|
||||
}
|
||||
@@ -351,7 +353,7 @@ public class FolderAnimationManager {
|
||||
int previewItemOffsetX, int previewItemOffsetY) {
|
||||
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
|
||||
boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
|
||||
final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
|
||||
final List<View> itemsInPreview = getPreviewIconsOnPage(
|
||||
isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage());
|
||||
final int numItemsInPreview = itemsInPreview.size();
|
||||
final int numItemsInFirstPagePreview = isOnFirstPage
|
||||
@@ -361,48 +363,49 @@ public class FolderAnimationManager {
|
||||
|
||||
ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
|
||||
for (int i = 0; i < numItemsInPreview; ++i) {
|
||||
final BubbleTextView btv = itemsInPreview.get(i);
|
||||
CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams();
|
||||
final View v = itemsInPreview.get(i);
|
||||
CellLayoutLayoutParams vLp = (CellLayoutLayoutParams) v.getLayoutParams();
|
||||
|
||||
// Calculate the final values in the LayoutParams.
|
||||
btvLp.isLockedToGrid = true;
|
||||
cwc.setupLp(btv);
|
||||
vLp.isLockedToGrid = true;
|
||||
cwc.setupLp(v);
|
||||
|
||||
// Match scale of icons in the preview of the items on the first page.
|
||||
float previewScale = rule.scaleForItem(numItemsInFirstPagePreview);
|
||||
float previewSize = rule.getIconSize() * previewScale;
|
||||
float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
|
||||
float baseIconSize = getBubbleTextView(v).getIconSize();
|
||||
float iconScale = previewSize / baseIconSize;
|
||||
|
||||
final float initialScale = iconScale / folderScale;
|
||||
final float finalScale = 1f;
|
||||
float scale = mIsOpening ? initialScale : finalScale;
|
||||
btv.setScaleX(scale);
|
||||
btv.setScaleY(scale);
|
||||
v.setScaleX(scale);
|
||||
v.setScaleY(scale);
|
||||
|
||||
// Match positions of the icons in the folder with their positions in the preview
|
||||
rule.computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, mTmpParams);
|
||||
// The PreviewLayoutRule assumes that the icon size takes up the entire width so we
|
||||
// offset by the actual size.
|
||||
int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
|
||||
int iconOffsetX = (int) ((vLp.width - baseIconSize) * iconScale) / 2;
|
||||
|
||||
final int previewPosX =
|
||||
(int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
|
||||
final float paddingTop = btv.getPaddingTop() * iconScale;
|
||||
final float paddingTop = v.getPaddingTop() * iconScale;
|
||||
final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY - paddingTop)
|
||||
/ folderScale);
|
||||
|
||||
final float xDistance = previewPosX - btvLp.x;
|
||||
final float yDistance = previewPosY - btvLp.y;
|
||||
final float xDistance = previewPosX - vLp.x;
|
||||
final float yDistance = previewPosY - vLp.y;
|
||||
|
||||
Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
|
||||
Animator translationX = getAnimator(v, View.TRANSLATION_X, xDistance, 0f);
|
||||
translationX.setInterpolator(previewItemInterpolator);
|
||||
play(animatorSet, translationX);
|
||||
|
||||
Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
|
||||
Animator translationY = getAnimator(v, View.TRANSLATION_Y, yDistance, 0f);
|
||||
translationY.setInterpolator(previewItemInterpolator);
|
||||
play(animatorSet, translationY);
|
||||
|
||||
Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
|
||||
Animator scaleAnimator = getAnimator(v, SCALE_PROPERTY, initialScale, finalScale);
|
||||
scaleAnimator.setInterpolator(previewItemInterpolator);
|
||||
play(animatorSet, scaleAnimator);
|
||||
|
||||
@@ -426,20 +429,20 @@ public class FolderAnimationManager {
|
||||
super.onAnimationStart(animation);
|
||||
// Necessary to initialize values here because of the start delay.
|
||||
if (mIsOpening) {
|
||||
btv.setTranslationX(xDistance);
|
||||
btv.setTranslationY(yDistance);
|
||||
btv.setScaleX(initialScale);
|
||||
btv.setScaleY(initialScale);
|
||||
v.setTranslationX(xDistance);
|
||||
v.setTranslationY(yDistance);
|
||||
v.setScaleX(initialScale);
|
||||
v.setScaleY(initialScale);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
btv.setTranslationX(0.0f);
|
||||
btv.setTranslationY(0.0f);
|
||||
btv.setScaleX(1f);
|
||||
btv.setScaleY(1f);
|
||||
v.setTranslationX(0.0f);
|
||||
v.setTranslationY(0.0f);
|
||||
v.setScaleX(1f);
|
||||
v.setScaleY(1f);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -482,4 +485,15 @@ public class FolderAnimationManager {
|
||||
? ObjectAnimator.ofArgb(drawable, property, v1, v2)
|
||||
: ObjectAnimator.ofArgb(drawable, property, v2, v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link com.android.launcher3.BubbleTextView} from an icon. In some cases the
|
||||
* BubbleTextView is the whole icon itself, while in others it is contained within the view and
|
||||
* only serves to store the title text.
|
||||
*/
|
||||
private BubbleTextView getBubbleTextView(View v) {
|
||||
return v instanceof AppPairIcon
|
||||
? ((AppPairIcon) v).getTitleTextView()
|
||||
: (BubbleTextView) v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ 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.AppPairInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo.FolderListener;
|
||||
import com.android.launcher3.model.data.FolderInfo.LabelState;
|
||||
@@ -118,7 +119,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
ClippedFolderIconLayoutRule mPreviewLayoutRule;
|
||||
private PreviewItemManager mPreviewItemManager;
|
||||
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
|
||||
private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
|
||||
private List<ItemInfo> mCurrentPreviewItems = new ArrayList<>();
|
||||
|
||||
boolean mAnimating = false;
|
||||
|
||||
@@ -215,7 +216,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
|
||||
// Keep the notification dot up to date with the sum of all the content's dots.
|
||||
FolderDotInfo folderDotInfo = new FolderDotInfo();
|
||||
for (WorkspaceItemInfo si : folderInfo.getContents()) {
|
||||
for (ItemInfo si : folderInfo.getContents()) {
|
||||
folderDotInfo.addDotInfo(activity.getDotInfoForItem(si));
|
||||
}
|
||||
icon.setDotInfo(folderDotInfo);
|
||||
@@ -261,20 +262,18 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
|
||||
private boolean willAcceptItem(ItemInfo item) {
|
||||
final int itemType = item.itemType;
|
||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
|
||||
item != mInfo && !mFolder.isOpen());
|
||||
return (Folder.willAcceptItemType(itemType) && item != mInfo && !mFolder.isOpen());
|
||||
}
|
||||
|
||||
public boolean acceptDrop(ItemInfo dragInfo) {
|
||||
return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
|
||||
}
|
||||
|
||||
public void addItem(WorkspaceItemInfo item) {
|
||||
public void addItem(ItemInfo item) {
|
||||
mInfo.add(item, true);
|
||||
}
|
||||
|
||||
public void removeItem(WorkspaceItemInfo item, boolean animate) {
|
||||
public void removeItem(ItemInfo item, boolean animate) {
|
||||
mInfo.remove(item, animate);
|
||||
}
|
||||
|
||||
@@ -287,8 +286,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
|
||||
if (SPRING_LOADING_ENABLED &&
|
||||
((dragInfo instanceof WorkspaceItemFactory)
|
||||
|| (dragInfo instanceof WorkspaceItemInfo)
|
||||
|| (dragInfo instanceof PendingAddShortcutInfo))) {
|
||||
|| (dragInfo instanceof PendingAddShortcutInfo)
|
||||
|| Folder.willAccept(dragInfo))) {
|
||||
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
|
||||
}
|
||||
}
|
||||
@@ -303,8 +302,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
return mPreviewItemManager.prepareCreateAnimation(destView);
|
||||
}
|
||||
|
||||
public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView,
|
||||
final WorkspaceItemInfo srcInfo, final DragObject d, Rect dstRect,
|
||||
public void performCreateAnimation(final ItemInfo destInfo, final View destView,
|
||||
final ItemInfo srcInfo, final DragObject d, Rect dstRect,
|
||||
float scaleRelativeToDragLayer) {
|
||||
final DragView srcView = d.dragView;
|
||||
prepareCreateAnimation(destView);
|
||||
@@ -330,7 +329,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
mOpenAlarm.cancelAlarm();
|
||||
}
|
||||
|
||||
private void onDrop(final WorkspaceItemInfo item, DragObject d, Rect finalRect,
|
||||
private void onDrop(final ItemInfo item, DragObject d, Rect finalRect,
|
||||
float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
|
||||
item.cellX = -1;
|
||||
item.cellY = -1;
|
||||
@@ -361,7 +360,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
|
||||
boolean itemAdded = false;
|
||||
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
|
||||
List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
|
||||
List<ItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
|
||||
mInfo.add(item, index, false);
|
||||
mCurrentPreviewItems.clear();
|
||||
mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
|
||||
@@ -422,7 +421,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
FolderNameInfos nameInfos = new FolderNameInfos();
|
||||
Executors.MODEL_EXECUTOR.post(() -> {
|
||||
d.folderNameProvider.getSuggestedFolderName(
|
||||
getContext(), mInfo.getContents(), nameInfos);
|
||||
getContext(), mInfo.getAppContents(), nameInfos);
|
||||
postDelayed(() -> {
|
||||
setLabelSuggestion(nameInfos, d.logInstanceId);
|
||||
invalidate();
|
||||
@@ -475,15 +474,21 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
|
||||
|
||||
public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
|
||||
WorkspaceItemInfo item;
|
||||
ItemInfo item;
|
||||
if (d.dragInfo instanceof WorkspaceItemFactory) {
|
||||
// Came from all apps -- make a copy
|
||||
item = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(getContext());
|
||||
} else if (d.dragSource instanceof BaseItemDragListener){
|
||||
// Came from a different window -- make a copy
|
||||
item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
|
||||
if (d.dragInfo instanceof AppPairInfo) {
|
||||
// dragged item is app pair
|
||||
item = new AppPairInfo((AppPairInfo) d.dragInfo);
|
||||
} else {
|
||||
// dragged item is WorkspaceItemInfo
|
||||
item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
|
||||
}
|
||||
} else {
|
||||
item = (WorkspaceItemInfo) d.dragInfo;
|
||||
item = d.dragInfo;
|
||||
}
|
||||
mFolder.notifyDrop();
|
||||
onDrop(item, d, null, 1.0f,
|
||||
@@ -665,7 +670,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
/**
|
||||
* Returns the list of items which should be visible in the preview
|
||||
*/
|
||||
public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) {
|
||||
public List<ItemInfo> getPreviewItemsOnPage(int page) {
|
||||
return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.getContents());
|
||||
}
|
||||
|
||||
@@ -690,12 +695,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
/**
|
||||
* Updates the preview items which match the provided condition
|
||||
*/
|
||||
public void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
|
||||
public void updatePreviewItems(Predicate<ItemInfo> itemCheck) {
|
||||
mPreviewItemManager.updatePreviewItems(itemCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdd(WorkspaceItemInfo item, int rank) {
|
||||
public void onAdd(ItemInfo item, int rank) {
|
||||
updatePreviewItems(false);
|
||||
boolean wasDotted = mDotInfo.hasDot();
|
||||
mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
|
||||
@@ -707,7 +712,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(List<WorkspaceItemInfo> items) {
|
||||
public void onRemove(List<ItemInfo> items) {
|
||||
updatePreviewItems(false);
|
||||
boolean wasDotted = mDotInfo.hasDot();
|
||||
items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
|
||||
@@ -718,6 +723,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleChanged(CharSequence title) {
|
||||
mFolderName.setText(title);
|
||||
setContentDescription(getAccessiblityTitle(title));
|
||||
|
||||
@@ -41,8 +41,10 @@ import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.apppairs.AppPairIcon;
|
||||
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.pageindicators.PageIndicatorDots;
|
||||
@@ -148,7 +150,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
/**
|
||||
* Binds items to the layout.
|
||||
*/
|
||||
public void bindItems(List<WorkspaceItemInfo> items) {
|
||||
public void bindItems(List<ItemInfo> items) {
|
||||
if (mViewsBound) {
|
||||
unbindItems();
|
||||
}
|
||||
@@ -164,8 +166,11 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
CellLayout page = (CellLayout) getChildAt(i);
|
||||
ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets();
|
||||
for (int j = container.getChildCount() - 1; j >= 0; j--) {
|
||||
container.getChildAt(j).setVisibility(View.VISIBLE);
|
||||
mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j));
|
||||
View iconView = container.getChildAt(j);
|
||||
iconView.setVisibility(View.VISIBLE);
|
||||
if (iconView instanceof BubbleTextView) {
|
||||
mViewCache.recycleView(R.layout.folder_application, iconView);
|
||||
}
|
||||
}
|
||||
page.removeAllViews();
|
||||
mViewCache.recycleView(R.layout.folder_page, page);
|
||||
@@ -185,7 +190,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
* Creates and adds an icon corresponding to the provided rank
|
||||
* @return the created icon
|
||||
*/
|
||||
public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) {
|
||||
public View createAndAddViewForRank(ItemInfo item, int rank) {
|
||||
View icon = createNewView(item);
|
||||
if (!mViewsBound) {
|
||||
return icon;
|
||||
@@ -200,7 +205,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
* Adds the {@param view} to the layout based on {@param rank} and updated the position
|
||||
* related attributes. It assumes that {@param item} is already attached to the view.
|
||||
*/
|
||||
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
|
||||
public void addViewForRank(View view, ItemInfo item, int rank) {
|
||||
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
|
||||
|
||||
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
|
||||
@@ -209,26 +214,36 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public View createNewView(WorkspaceItemInfo item) {
|
||||
public View createNewView(ItemInfo item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
final BubbleTextView textView = mViewCache.getView(
|
||||
R.layout.folder_application, getContext(), null);
|
||||
textView.applyFromWorkspaceItem(item);
|
||||
textView.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener());
|
||||
textView.setOnLongClickListener(mFolder);
|
||||
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
|
||||
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
|
||||
|
||||
final View icon;
|
||||
if (item instanceof AppPairInfo api) {
|
||||
// TODO (b/332607759): Make view cache work with app pair icons
|
||||
icon = AppPairIcon.inflateIcon(R.layout.folder_app_pair, ActivityContext.lookupContext(
|
||||
getContext()), null , api, BubbleTextView.DISPLAY_FOLDER);
|
||||
} else {
|
||||
icon = mViewCache.getView(R.layout.folder_application, getContext(), null);
|
||||
((BubbleTextView) icon).applyFromWorkspaceItem((WorkspaceItemInfo) item);
|
||||
}
|
||||
|
||||
icon.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener());
|
||||
icon.setOnLongClickListener(mFolder);
|
||||
icon.setOnFocusChangeListener(mFocusIndicatorHelper);
|
||||
|
||||
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) icon.getLayoutParams();
|
||||
if (lp == null) {
|
||||
textView.setLayoutParams(new CellLayoutLayoutParams(
|
||||
icon.setLayoutParams(new CellLayoutLayoutParams(
|
||||
item.cellX, item.cellY, item.spanX, item.spanY));
|
||||
} else {
|
||||
lp.setCellX(item.cellX);
|
||||
lp.setCellY(item.cellY);
|
||||
lp.cellHSpan = lp.cellVSpan = 1;
|
||||
}
|
||||
return textView;
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -497,13 +512,20 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
if (page != null) {
|
||||
ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
|
||||
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
|
||||
BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i));
|
||||
icon.verifyHighRes();
|
||||
View iconView = parent.getChildAt(i);
|
||||
Drawable d = null;
|
||||
if (iconView instanceof BubbleTextView btv) {
|
||||
btv.verifyHighRes();
|
||||
d = btv.getIcon();
|
||||
} else if (iconView instanceof AppPairIcon api) {
|
||||
api.verifyHighRes();
|
||||
d = api.getIconDrawableArea().getDrawable();
|
||||
}
|
||||
|
||||
// Set the callback back to the actual icon, in case
|
||||
// it was captured by the FolderIcon
|
||||
Drawable d = icon.getIcon();
|
||||
if (d != null) {
|
||||
d.setCallback(icon);
|
||||
d.setCallback(iconView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class LauncherDelegate {
|
||||
FolderInfo info = folder.mInfo;
|
||||
if (itemCount <= 1) {
|
||||
View newIcon = null;
|
||||
WorkspaceItemInfo finalItem = null;
|
||||
ItemInfo finalItem = null;
|
||||
|
||||
if (itemCount == 1) {
|
||||
// Move the item from the folder to the workspace, in the position of the
|
||||
|
||||
@@ -17,7 +17,7 @@ package com.android.launcher3.folder;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
|
||||
/**
|
||||
* Manages the parameters used to draw a Folder preview item.
|
||||
@@ -30,7 +30,7 @@ class PreviewItemDrawingParams {
|
||||
public FolderPreviewItemAnim anim;
|
||||
public boolean hidden;
|
||||
public Drawable drawable;
|
||||
public WorkspaceItemInfo item;
|
||||
public ItemInfo item;
|
||||
|
||||
PreviewItemDrawingParams(float transX, float transY, float scale) {
|
||||
this.transX = transX;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3.folder;
|
||||
|
||||
import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
@@ -41,7 +42,12 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.apppairs.AppPairIcon;
|
||||
import com.android.launcher3.apppairs.AppPairIconDrawingParams;
|
||||
import com.android.launcher3.apppairs.AppPairIconGraphic;
|
||||
import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.util.Themes;
|
||||
@@ -125,7 +131,9 @@ public class PreviewItemManager {
|
||||
}
|
||||
|
||||
Drawable prepareCreateAnimation(final View destView) {
|
||||
Drawable animateDrawable = ((BubbleTextView) destView).getIcon();
|
||||
Drawable animateDrawable = destView instanceof AppPairIcon
|
||||
? ((AppPairIcon) destView).getIconDrawableArea().getDrawable()
|
||||
: ((BubbleTextView) destView).getIcon();
|
||||
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
|
||||
destView.getMeasuredWidth());
|
||||
mReferenceDrawable = animateDrawable;
|
||||
@@ -258,7 +266,7 @@ public class PreviewItemManager {
|
||||
}
|
||||
|
||||
void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
|
||||
List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page);
|
||||
List<ItemInfo> items = mIcon.getPreviewItemsOnPage(page);
|
||||
|
||||
// We adjust the size of the list to match the number of items in the preview.
|
||||
while (items.size() < params.size()) {
|
||||
@@ -328,16 +336,18 @@ public class PreviewItemManager {
|
||||
mNumOfPrevItems = numOfPrevItemsAux;
|
||||
}
|
||||
|
||||
void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
|
||||
void updatePreviewItems(Predicate<ItemInfo> itemCheck) {
|
||||
boolean modified = false;
|
||||
for (PreviewItemDrawingParams param : mFirstPageParams) {
|
||||
if (itemCheck.test(param.item)) {
|
||||
if (itemCheck.test(param.item)
|
||||
|| (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) {
|
||||
setDrawable(param, param.item);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
for (PreviewItemDrawingParams param : mCurrentPageParams) {
|
||||
if (itemCheck.test(param.item)) {
|
||||
if (itemCheck.test(param.item)
|
||||
|| (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) {
|
||||
setDrawable(param, param.item);
|
||||
modified = true;
|
||||
}
|
||||
@@ -370,15 +380,14 @@ public class PreviewItemManager {
|
||||
* @param newItems The list of items in the new preview.
|
||||
* @param dropped The item that was dropped onto the FolderIcon.
|
||||
*/
|
||||
public void onDrop(List<WorkspaceItemInfo> oldItems, List<WorkspaceItemInfo> newItems,
|
||||
WorkspaceItemInfo dropped) {
|
||||
public void onDrop(List<ItemInfo> oldItems, List<ItemInfo> newItems, ItemInfo dropped) {
|
||||
int numItems = newItems.size();
|
||||
final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
|
||||
buildParamsForPage(0, params, false);
|
||||
|
||||
// New preview items for items that are moving in (except for the dropped item).
|
||||
List<WorkspaceItemInfo> moveIn = new ArrayList<>();
|
||||
for (WorkspaceItemInfo newItem : newItems) {
|
||||
List<ItemInfo> moveIn = new ArrayList<>();
|
||||
for (ItemInfo newItem : newItems) {
|
||||
if (!oldItems.contains(newItem) && !newItem.equals(dropped)) {
|
||||
moveIn.add(newItem);
|
||||
}
|
||||
@@ -401,10 +410,10 @@ public class PreviewItemManager {
|
||||
}
|
||||
|
||||
// Old preview items that need to be moved out.
|
||||
List<WorkspaceItemInfo> moveOut = new ArrayList<>(oldItems);
|
||||
List<ItemInfo> moveOut = new ArrayList<>(oldItems);
|
||||
moveOut.removeAll(newItems);
|
||||
for (int i = 0; i < moveOut.size(); ++i) {
|
||||
WorkspaceItemInfo item = moveOut.get(i);
|
||||
ItemInfo item = moveOut.get(i);
|
||||
int oldIndex = oldItems.indexOf(item);
|
||||
PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
|
||||
updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems);
|
||||
@@ -418,7 +427,7 @@ public class PreviewItemManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item,
|
||||
private void updateTransitionParam(final PreviewItemDrawingParams p, ItemInfo item,
|
||||
int prevIndex, int newIndex, int numItems) {
|
||||
setDrawable(p, item);
|
||||
|
||||
@@ -431,16 +440,24 @@ public class PreviewItemManager {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
|
||||
if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
|
||||
& ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
|
||||
PreloadIconDrawable drawable = newPendingIcon(mContext, item);
|
||||
p.drawable = drawable;
|
||||
} else {
|
||||
p.drawable = item.newIcon(mContext,
|
||||
Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
|
||||
public void setDrawable(PreviewItemDrawingParams p, ItemInfo item) {
|
||||
if (item instanceof WorkspaceItemInfo wii) {
|
||||
if (wii.hasPromiseIconUi() || (wii.runtimeStatusFlags
|
||||
& ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
|
||||
PreloadIconDrawable drawable = newPendingIcon(mContext, wii);
|
||||
p.drawable = drawable;
|
||||
} else {
|
||||
p.drawable = wii.newIcon(mContext,
|
||||
Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
|
||||
}
|
||||
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
|
||||
} else if (item instanceof AppPairInfo api) {
|
||||
AppPairIconDrawingParams appPairParams =
|
||||
new AppPairIconDrawingParams(mContext, DISPLAY_FOLDER);
|
||||
p.drawable = AppPairIconGraphic.composeDrawable(api, appPairParams);
|
||||
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
|
||||
}
|
||||
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
|
||||
|
||||
p.item = item;
|
||||
// Set the callback to FolderIcon as it is responsible to drawing the icon. The
|
||||
// callback will be released when the folder is opened.
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.CollectionInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
@@ -259,10 +260,15 @@ public class BgDataModel {
|
||||
itemsIdMap.put(item.id, item);
|
||||
switch (item.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
|
||||
collections.put(item.id, (CollectionInfo) item);
|
||||
collections.put(item.id, (FolderInfo) item);
|
||||
workspaceItems.add(item);
|
||||
break;
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
|
||||
collections.put(item.id, (AppPairInfo) item);
|
||||
// Fall through here. App pairs are both containers (like folders) and containable
|
||||
// items (can be placed in folders). So we need to add app pairs to the folders
|
||||
// array (above) but also verify the existence of their container, like regular
|
||||
// apps (below).
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
|
||||
@@ -277,7 +283,7 @@ public class BgDataModel {
|
||||
Log.e(TAG, msg);
|
||||
}
|
||||
} else {
|
||||
findOrMakeFolder(item.container).add((WorkspaceItemInfo) item);
|
||||
findOrMakeFolder(item.container).add(item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -115,7 +115,7 @@ public class FirstScreenBroadcast {
|
||||
for (ItemInfo info : firstScreenItems) {
|
||||
if (info instanceof CollectionInfo ci) {
|
||||
String collectionItemInfoPackage;
|
||||
for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getContents())) {
|
||||
for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getAppContents())) {
|
||||
collectionItemInfoPackage = getPackageName(collectionItemInfo);
|
||||
if (collectionItemInfoPackage != null
|
||||
&& packages.contains(collectionItemInfoPackage)) {
|
||||
|
||||
@@ -81,7 +81,6 @@ import com.android.launcher3.model.data.CollectionInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.IconRequestInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.pm.InstallSessionHelper;
|
||||
@@ -494,9 +493,7 @@ public class LoaderTask implements Runnable {
|
||||
}
|
||||
|
||||
appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
|
||||
// Fetch hi-res icons if needed.
|
||||
appPair.getContents().stream().filter(ItemInfoWithIcon::usingLowResIcon)
|
||||
.forEach(member -> mIconCache.getTitleAndIcon(member, false));
|
||||
appPair.fetchHiResIconsIfNeeded(mIconCache);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,12 +563,16 @@ public class LoaderTask implements Runnable {
|
||||
// Ranks are the source of truth for folder items, so cellX and cellY can be
|
||||
// ignored for now. Database will be updated once user manually modifies folder.
|
||||
for (int rank = 0; rank < size; ++rank) {
|
||||
WorkspaceItemInfo info = folder.getContents().get(rank);
|
||||
ItemInfo info = folder.getContents().get(rank);
|
||||
info.rank = rank;
|
||||
|
||||
if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION
|
||||
if (info instanceof WorkspaceItemInfo wii
|
||||
&& wii.usingLowResIcon()
|
||||
&& wii.itemType == Favorites.ITEM_TYPE_APPLICATION
|
||||
&& verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) {
|
||||
mIconCache.getTitleAndIcon(info, false);
|
||||
mIconCache.getTitleAndIcon(wii, false);
|
||||
} else if (info instanceof AppPairInfo api) {
|
||||
api.fetchHiResIconsIfNeeded(mIconCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -788,7 +789,7 @@ public class LoaderTask implements Runnable {
|
||||
FolderNameInfos suggestionInfos = new FolderNameInfos();
|
||||
CollectionInfo info = mBgDataModel.collections.valueAt(i);
|
||||
if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) {
|
||||
provider.getSuggestedFolderName(mApp.getContext(), fi.getContents(),
|
||||
provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
|
||||
suggestionInfos);
|
||||
fi.suggestedFolderNames = suggestionInfos;
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ class WorkspaceItemProcessor(
|
||||
val folderInfo: FolderInfo = collection
|
||||
val newAppPair = AppPairInfo()
|
||||
// Move the placeholder's contents over to the new app pair.
|
||||
folderInfo.contents.forEach(newAppPair::add)
|
||||
folderInfo.getContents().forEach(newAppPair::add)
|
||||
collection = newAppPair
|
||||
// Remove the placeholder and add the app pair into the data model.
|
||||
bgDataModel.collections.remove(c.id)
|
||||
|
||||
@@ -18,11 +18,14 @@ package com.android.launcher3.model.data
|
||||
|
||||
import android.content.Context
|
||||
import com.android.launcher3.LauncherSettings
|
||||
import com.android.launcher3.icons.IconCache
|
||||
import com.android.launcher3.logger.LauncherAtom
|
||||
import com.android.launcher3.views.ActivityContext
|
||||
|
||||
/** A type of app collection that launches multiple apps into split screen. */
|
||||
class AppPairInfo() : CollectionInfo() {
|
||||
private var contents: ArrayList<WorkspaceItemInfo> = ArrayList()
|
||||
|
||||
init {
|
||||
itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
|
||||
}
|
||||
@@ -33,11 +36,28 @@ class AppPairInfo() : CollectionInfo() {
|
||||
add(app2)
|
||||
}
|
||||
|
||||
/** Adds an element to the contents array. */
|
||||
override fun add(item: WorkspaceItemInfo) {
|
||||
/** Creates a new AppPairInfo that is a copy of the provided one. */
|
||||
constructor(appPairInfo: AppPairInfo) : this() {
|
||||
contents = appPairInfo.contents.clone() as ArrayList<WorkspaceItemInfo>
|
||||
copyFrom(appPairInfo)
|
||||
}
|
||||
|
||||
/** Adds an element to the contents ArrayList. */
|
||||
override fun add(item: ItemInfo) {
|
||||
if (item !is WorkspaceItemInfo) {
|
||||
throw RuntimeException("tried to add an illegal type into an app pair")
|
||||
}
|
||||
|
||||
contents.add(item)
|
||||
}
|
||||
|
||||
/** Returns the app pair's member apps as an ArrayList of [ItemInfo]. */
|
||||
override fun getContents(): ArrayList<ItemInfo> =
|
||||
ArrayList(contents.stream().map { it as ItemInfo }.toList())
|
||||
|
||||
/** Returns the app pair's member apps as an ArrayList of [WorkspaceItemInfo]. */
|
||||
override fun getAppContents(): ArrayList<WorkspaceItemInfo> = contents
|
||||
|
||||
/** Returns the first app in the pair. */
|
||||
fun getFirstApp() = contents[0]
|
||||
|
||||
@@ -50,7 +70,9 @@ class AppPairInfo() : CollectionInfo() {
|
||||
/** Checks if the app pair is launchable at the current screen size. */
|
||||
fun isLaunchable(context: Context) =
|
||||
(ActivityContext.lookupContext(context) as ActivityContext).getDeviceProfile().isTablet ||
|
||||
noneMatch { it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE) }
|
||||
getAppContents().stream().noneMatch {
|
||||
it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE)
|
||||
}
|
||||
|
||||
/** Generates an ItemInfo for logging. */
|
||||
override fun buildProto(cInfo: CollectionInfo?): LauncherAtom.ItemInfo {
|
||||
@@ -62,4 +84,11 @@ class AppPairInfo() : CollectionInfo() {
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build()
|
||||
}
|
||||
|
||||
/** Fetches high-res icons for member apps if needed. */
|
||||
fun fetchHiResIconsIfNeeded(iconCache: IconCache) {
|
||||
getAppContents().stream().filter(ItemInfoWithIcon::usingLowResIcon).forEach { member ->
|
||||
iconCache.getTitleAndIcon(member, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,19 +22,21 @@ import com.android.launcher3.util.ContentWriter
|
||||
import java.util.function.Predicate
|
||||
|
||||
abstract class CollectionInfo : ItemInfo() {
|
||||
var contents: ArrayList<WorkspaceItemInfo> = ArrayList()
|
||||
/** Adds an ItemInfo to the collection. Throws if given an illegal type. */
|
||||
abstract fun add(item: ItemInfo)
|
||||
|
||||
abstract fun add(item: WorkspaceItemInfo)
|
||||
/** Returns the collection's contents as an ArrayList of [ItemInfo]. */
|
||||
abstract fun getContents(): ArrayList<ItemInfo>
|
||||
|
||||
/**
|
||||
* Returns the collection's contents as an ArrayList of [WorkspaceItemInfo]. Does not include
|
||||
* other collection [ItemInfo]s that are inside this collection; rather, it should collect
|
||||
* *their* contents and adds them to the ArrayList.
|
||||
*/
|
||||
abstract fun getAppContents(): ArrayList<WorkspaceItemInfo>
|
||||
|
||||
/** Convenience function. Checks contents to see if any match a given predicate. */
|
||||
fun anyMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
|
||||
return contents.stream().anyMatch(matcher)
|
||||
}
|
||||
|
||||
/** Convenience function. Returns true if none of the contents match a given predicate. */
|
||||
fun noneMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
|
||||
return contents.stream().noneMatch(matcher)
|
||||
}
|
||||
fun anyMatch(matcher: Predicate<ItemInfo>) = getContents().stream().anyMatch(matcher)
|
||||
|
||||
override fun onAddToDatabase(writer: ContentWriter) {
|
||||
super.onAddToDatabase(writer)
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderNameInfos;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.Attribute;
|
||||
@@ -98,14 +99,20 @@ public class FolderInfo extends CollectionInfo {
|
||||
|
||||
public FolderNameInfos suggestedFolderNames;
|
||||
|
||||
/**
|
||||
* The apps and shortcuts
|
||||
*/
|
||||
private final ArrayList<ItemInfo> contents = new ArrayList<>();
|
||||
|
||||
private ArrayList<FolderListener> mListeners = new ArrayList<>();
|
||||
|
||||
public FolderInfo() {
|
||||
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
|
||||
}
|
||||
|
||||
/** Adds a app or shortcut to the contents array without animation. */
|
||||
public void add(@NonNull WorkspaceItemInfo item) {
|
||||
/** Adds a app or shortcut to the contents ArrayList without animation. */
|
||||
@Override
|
||||
public void add(@NonNull ItemInfo item) {
|
||||
add(item, false /* animate */);
|
||||
}
|
||||
|
||||
@@ -114,14 +121,18 @@ public class FolderInfo extends CollectionInfo {
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
public void add(WorkspaceItemInfo item, boolean animate) {
|
||||
public void add(ItemInfo item, boolean animate) {
|
||||
add(item, getContents().size(), animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an app or shortcut for a specified rank.
|
||||
*/
|
||||
public void add(WorkspaceItemInfo item, int rank, boolean animate) {
|
||||
public void add(ItemInfo item, int rank, boolean animate) {
|
||||
if (!Folder.willAccept(item)) {
|
||||
throw new RuntimeException("tried to add an illegal type into a folder");
|
||||
}
|
||||
|
||||
rank = Utilities.boundToRange(rank, 0, getContents().size());
|
||||
getContents().add(rank, item);
|
||||
for (int i = 0; i < mListeners.size(); i++) {
|
||||
@@ -135,21 +146,49 @@ public class FolderInfo extends CollectionInfo {
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
public void remove(WorkspaceItemInfo item, boolean animate) {
|
||||
public void remove(ItemInfo item, boolean animate) {
|
||||
removeAll(Collections.singletonList(item), animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all matching app or shortcut. Does not change the DB.
|
||||
*/
|
||||
public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
|
||||
getContents().removeAll(items);
|
||||
public void removeAll(List<ItemInfo> items, boolean animate) {
|
||||
contents.removeAll(items);
|
||||
for (int i = 0; i < mListeners.size(); i++) {
|
||||
mListeners.get(i).onRemove(items);
|
||||
}
|
||||
itemsChanged(animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the folder's contents as an ArrayList of {@link ItemInfo}. Includes
|
||||
* {@link WorkspaceItemInfo} and {@link AppPairInfo}s.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public ArrayList<ItemInfo> getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the folder's contents as an ArrayList of {@link WorkspaceItemInfo}. Note: Does not
|
||||
* return any {@link AppPairInfo}s contained in the folder, instead collects *their* contents
|
||||
* and adds them to the ArrayList.
|
||||
*/
|
||||
@Override
|
||||
public ArrayList<WorkspaceItemInfo> getAppContents() {
|
||||
ArrayList<WorkspaceItemInfo> workspaceItemInfos = new ArrayList<>();
|
||||
for (ItemInfo item : contents) {
|
||||
if (item instanceof WorkspaceItemInfo wii) {
|
||||
workspaceItemInfos.add(wii);
|
||||
} else if (item instanceof AppPairInfo api) {
|
||||
workspaceItemInfos.addAll(api.getAppContents());
|
||||
}
|
||||
}
|
||||
return workspaceItemInfos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddToDatabase(@NonNull ContentWriter writer) {
|
||||
super.onAddToDatabase(writer);
|
||||
@@ -171,9 +210,11 @@ public class FolderInfo extends CollectionInfo {
|
||||
}
|
||||
|
||||
public interface FolderListener {
|
||||
void onAdd(WorkspaceItemInfo item, int rank);
|
||||
void onRemove(List<WorkspaceItemInfo> item);
|
||||
void onAdd(ItemInfo item, int rank);
|
||||
void onRemove(List<ItemInfo> item);
|
||||
void onItemsChanged(boolean animate);
|
||||
void onTitleChanged(CharSequence title);
|
||||
|
||||
}
|
||||
|
||||
public boolean hasOption(int optionFlag) {
|
||||
@@ -246,6 +287,10 @@ public class FolderInfo extends CollectionInfo {
|
||||
if (modelWriter != null) {
|
||||
modelWriter.updateItemInDatabase(this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mListeners.size(); i++) {
|
||||
mListeners.get(i).onTitleChanged(title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,10 +308,17 @@ public class FolderInfo extends CollectionInfo {
|
||||
public ItemInfo makeShallowCopy() {
|
||||
FolderInfo folderInfo = new FolderInfo();
|
||||
folderInfo.copyFrom(this);
|
||||
folderInfo.setContents(this.getContents());
|
||||
return folderInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(@NonNull ItemInfo info) {
|
||||
super.copyFrom(info);
|
||||
if (info instanceof FolderInfo fi) {
|
||||
contents.addAll(fi.getContents());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of the accepted suggestion.
|
||||
*/
|
||||
|
||||
@@ -82,7 +82,6 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
||||
};
|
||||
protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
|
||||
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
|
||||
private static final float VIEW_NO_SCALE = 1f;
|
||||
private static final int DEFAULT_DURATION = 300;
|
||||
|
||||
protected final T mActivityContext;
|
||||
@@ -129,9 +128,13 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
||||
protected @Nullable OnCloseListener mOnCloseBeginListener;
|
||||
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
|
||||
|
||||
protected final AnimatedFloat mSlideInViewScale =
|
||||
new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE);
|
||||
protected boolean mIsBackProgressing;
|
||||
/**
|
||||
* How far through a "user initiated dismissal" the UI is. e.g. Predictive back, swipe to home,
|
||||
* 0 is regular state, 1 is fully dismissed.
|
||||
*/
|
||||
protected final AnimatedFloat mSwipeToDismissProgress =
|
||||
new AnimatedFloat(this::onUserSwipeToDismissProgressChanged, 0f);
|
||||
protected boolean mIsDismissInProgress;
|
||||
private @Nullable Drawable mContentBackground;
|
||||
private @Nullable View mContentBackgroundParentView;
|
||||
|
||||
@@ -287,29 +290,30 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
||||
final float progress = backEvent.getProgress();
|
||||
float deceleratedProgress =
|
||||
Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
|
||||
mIsBackProgressing = progress > 0f;
|
||||
mSlideInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE
|
||||
+ (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress));
|
||||
mSwipeToDismissProgress.updateValue(deceleratedProgress);
|
||||
}
|
||||
|
||||
protected void onScaleProgressChanged() {
|
||||
float scaleProgress = mSlideInViewScale.value;
|
||||
SCALE_PROPERTY.set(this, scaleProgress);
|
||||
setClipChildren(!mIsBackProgressing);
|
||||
setClipToPadding(!mIsBackProgressing);
|
||||
mContent.setClipChildren(!mIsBackProgressing);
|
||||
mContent.setClipToPadding(!mIsBackProgressing);
|
||||
protected void onUserSwipeToDismissProgressChanged() {
|
||||
float progress = mSwipeToDismissProgress.value;
|
||||
mIsDismissInProgress = progress > 0f;
|
||||
|
||||
float scale = PREDICTIVE_BACK_MIN_SCALE + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1f - progress);
|
||||
SCALE_PROPERTY.set(this, scale);
|
||||
setClipChildren(!mIsDismissInProgress);
|
||||
setClipToPadding(!mIsDismissInProgress);
|
||||
mContent.setClipChildren(!mIsDismissInProgress);
|
||||
mContent.setClipToPadding(!mIsDismissInProgress);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
super.onBackCancelled();
|
||||
animateSlideInViewToNoScale();
|
||||
animateSwipeToDismissProgressToStart();
|
||||
}
|
||||
|
||||
protected void animateSlideInViewToNoScale() {
|
||||
mSlideInViewScale.animateToValue(1f)
|
||||
protected void animateSwipeToDismissProgressToStart() {
|
||||
mSwipeToDismissProgress.animateToValue(0f)
|
||||
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
|
||||
.start();
|
||||
}
|
||||
@@ -340,7 +344,7 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
||||
mContentBackgroundParentView.getTop() + (int) mContent.getTranslationY(),
|
||||
mContentBackgroundParentView.getRight(),
|
||||
mContentBackgroundParentView.getBottom()
|
||||
+ (mIsBackProgressing ? getBottomOffsetPx() : 0));
|
||||
+ (mIsDismissInProgress ? getBottomOffsetPx() : 0));
|
||||
mContentBackground.draw(canvas);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ public class WidgetsEduView extends AbstractSlideInView<BaseActivity> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScaleProgressChanged() {
|
||||
super.onScaleProgressChanged();
|
||||
setTranslationY(getMeasuredHeight() * (1 - mSlideInViewScale.value) / 2);
|
||||
protected void onUserSwipeToDismissProgressChanged() {
|
||||
super.onUserSwipeToDismissProgressChanged();
|
||||
setTranslationY(getMeasuredHeight() * (mSwipeToDismissProgress.value / 2));
|
||||
}
|
||||
|
||||
private void show() {
|
||||
|
||||
@@ -26,7 +26,6 @@ import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
@@ -39,7 +38,6 @@ import android.view.ViewPropertyAnimator;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
@@ -88,7 +86,6 @@ public class WidgetCell extends LinearLayout {
|
||||
private Size mPreviewContainerSize = new Size(0, 0);
|
||||
private FrameLayout mWidgetImageContainer;
|
||||
private WidgetImageView mWidgetImage;
|
||||
private ImageView mWidgetBadge;
|
||||
private TextView mWidgetName;
|
||||
private TextView mWidgetDims;
|
||||
private TextView mWidgetDescription;
|
||||
@@ -142,7 +139,6 @@ public class WidgetCell extends LinearLayout {
|
||||
|
||||
mWidgetImageContainer = findViewById(R.id.widget_preview_container);
|
||||
mWidgetImage = findViewById(R.id.widget_preview);
|
||||
mWidgetBadge = findViewById(R.id.widget_badge);
|
||||
mWidgetName = findViewById(R.id.widget_name);
|
||||
mWidgetDims = findViewById(R.id.widget_dims);
|
||||
mWidgetDescription = findViewById(R.id.widget_description);
|
||||
@@ -182,8 +178,6 @@ public class WidgetCell extends LinearLayout {
|
||||
mWidgetImage.animate().cancel();
|
||||
mWidgetImage.setDrawable(null);
|
||||
mWidgetImage.setVisibility(View.VISIBLE);
|
||||
mWidgetBadge.setImageDrawable(null);
|
||||
mWidgetBadge.setVisibility(View.GONE);
|
||||
mWidgetName.setText(null);
|
||||
mWidgetDims.setText(null);
|
||||
mWidgetDescription.setText(null);
|
||||
@@ -397,17 +391,6 @@ public class WidgetCell extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to show the badge when the widget is in the recommended section
|
||||
*/
|
||||
public void showBadge() {
|
||||
if (Process.myUserHandle().equals(mItem.user)) {
|
||||
mWidgetBadge.setVisibility(View.GONE);
|
||||
} else {
|
||||
mWidgetBadge.setVisibility(View.VISIBLE);
|
||||
mWidgetBadge.setImageResource(R.drawable.ic_work_app_badge);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the long description displayed below each widget.
|
||||
*
|
||||
|
||||
@@ -24,8 +24,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* View that draws a bitmap horizontally centered. If the image width is greater than the view
|
||||
* width, the image is scaled down appropriately.
|
||||
@@ -33,8 +31,6 @@ import com.android.launcher3.R;
|
||||
public class WidgetImageView extends View {
|
||||
|
||||
private final RectF mDstRectF = new RectF();
|
||||
private final int mBadgeMargin;
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
public WidgetImageView(Context context) {
|
||||
@@ -47,9 +43,6 @@ public class WidgetImageView extends View {
|
||||
|
||||
public WidgetImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
mBadgeMargin = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.profile_badge_margin);
|
||||
}
|
||||
|
||||
/** Set the drawable to use for this view. */
|
||||
|
||||
@@ -280,6 +280,6 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
@Override
|
||||
public void addHintCloseAnim(
|
||||
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
|
||||
target.setInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom), interpolator);
|
||||
target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.android.launcher3.widget.picker;
|
||||
|
||||
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
|
||||
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_DIALOG_SEEN;
|
||||
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
|
||||
@@ -814,8 +813,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
@Override
|
||||
public void addHintCloseAnim(
|
||||
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
|
||||
target.setFloat(getRecyclerView(), VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
|
||||
target.setViewAlpha(getRecyclerView(), 0.5f, interpolator);
|
||||
target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -911,7 +909,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
public void onBackInvoked() {
|
||||
if (mIsInSearchMode) {
|
||||
mSearchBar.reset();
|
||||
animateSlideInViewToNoScale();
|
||||
animateSwipeToDismissProgressToStart();
|
||||
} else {
|
||||
super.onBackInvoked();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
|
||||
private ScrollView mRightPaneScrollView;
|
||||
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
|
||||
|
||||
private boolean mOldIsBackSwipeProgressing;
|
||||
private boolean mOldIsSwipeToDismissInProgress;
|
||||
private int mActivePage = -1;
|
||||
private PackageUserKey mSelectedHeader;
|
||||
|
||||
@@ -154,14 +154,14 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScaleProgressChanged() {
|
||||
super.onScaleProgressChanged();
|
||||
boolean isBackSwipeProgressing = mSlideInViewScale.value > 0;
|
||||
if (isBackSwipeProgressing == mOldIsBackSwipeProgressing) {
|
||||
protected void onUserSwipeToDismissProgressChanged() {
|
||||
super.onUserSwipeToDismissProgressChanged();
|
||||
boolean isSwipeToDismissInProgress = mSwipeToDismissProgress.value > 0;
|
||||
if (isSwipeToDismissInProgress == mOldIsSwipeToDismissInProgress) {
|
||||
return;
|
||||
}
|
||||
mOldIsBackSwipeProgressing = isBackSwipeProgressing;
|
||||
if (isBackSwipeProgressing) {
|
||||
mOldIsSwipeToDismissInProgress = isSwipeToDismissInProgress;
|
||||
if (isSwipeToDismissInProgress) {
|
||||
modifyAttributesOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent,
|
||||
CLIP_CHILDREN_FALSE_MODIFIER);
|
||||
modifyAttributesOnViewTree(mRightPaneScrollView, (ViewParent) mContent,
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.android.launcher3.allapps.ActivityAllAppsContainerView;
|
||||
import com.android.launcher3.allapps.SearchRecyclerView;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -40,7 +39,6 @@ public class LauncherIntentTest extends AbstractLauncherUiTest {
|
||||
public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
|
||||
|
||||
@Test
|
||||
@Ignore("b/329152799")
|
||||
public void testAllAppsIntent() {
|
||||
// setup by moving to home
|
||||
mLauncher.goHome();
|
||||
@@ -66,8 +64,6 @@ public class LauncherIntentTest extends AbstractLauncherUiTest {
|
||||
|
||||
// Highlights the search bar, then fills text to display the SearchView.
|
||||
private void moveToSearchView() {
|
||||
mLauncher.goHome().switchToAllApps();
|
||||
|
||||
// All Apps view should be loaded
|
||||
assertTrue("Launcher internal state is not All Apps",
|
||||
isInState(() -> LauncherState.ALL_APPS));
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.android.launcher3.icons.BaseIconFactory
|
||||
import com.android.launcher3.icons.FastBitmapDrawable
|
||||
import com.android.launcher3.icons.UserBadgeDrawable
|
||||
import com.android.launcher3.model.data.FolderInfo
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.util.ActivityContextWrapper
|
||||
import com.android.launcher3.util.FlagOp
|
||||
import com.android.launcher3.util.LauncherLayoutBuilder
|
||||
@@ -47,7 +47,7 @@ class PreviewItemManagerTest {
|
||||
|
||||
private lateinit var previewItemManager: PreviewItemManager
|
||||
private lateinit var context: Context
|
||||
private lateinit var folderItems: ArrayList<WorkspaceItemInfo>
|
||||
private lateinit var folderItems: ArrayList<ItemInfo>
|
||||
private lateinit var modelHelper: LauncherModelHelper
|
||||
private lateinit var folderIcon: FolderIcon
|
||||
|
||||
@@ -72,15 +72,17 @@ class PreviewItemManagerTest {
|
||||
.build()
|
||||
)
|
||||
.loadModelSync()
|
||||
folderItems = modelHelper.bgDataModel.collections.valueAt(0).contents
|
||||
folderItems = modelHelper.bgDataModel.collections.valueAt(0).getContents()
|
||||
folderIcon.mInfo = modelHelper.bgDataModel.collections.valueAt(0) as FolderInfo
|
||||
folderIcon.mInfo.contents = folderItems
|
||||
folderIcon.mInfo.getContents().addAll(folderItems)
|
||||
|
||||
// Use getAppContents() to "cast" contents to WorkspaceItemInfo so we can set bitmaps
|
||||
val folderApps = modelHelper.bgDataModel.collections.valueAt(0).getAppContents()
|
||||
// Set first icon to be themed.
|
||||
folderItems[0]
|
||||
folderApps[0]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
folderItems[0].bitmap.icon,
|
||||
folderApps[0].bitmap.icon,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
@@ -89,7 +91,7 @@ class PreviewItemManagerTest {
|
||||
)
|
||||
|
||||
// Set second icon to be non-themed.
|
||||
folderItems[1]
|
||||
folderApps[1]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
null,
|
||||
@@ -101,23 +103,21 @@ class PreviewItemManagerTest {
|
||||
)
|
||||
|
||||
// Set third icon to be themed with badge.
|
||||
folderItems[2]
|
||||
folderApps[2]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
folderItems[2].bitmap.icon,
|
||||
folderApps[2].bitmap.icon,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
previewItemManager.mIconSize
|
||||
)
|
||||
)
|
||||
folderItems[2].bitmap =
|
||||
folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
|
||||
// Set fourth icon to be non-themed with badge.
|
||||
folderItems[3].bitmap =
|
||||
folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
folderItems[3]
|
||||
folderApps[3].bitmap = folderApps[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
folderApps[3]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
null,
|
||||
|
||||
@@ -160,6 +160,6 @@ public class CacheDataUpdatedTaskTest {
|
||||
}
|
||||
|
||||
private List<WorkspaceItemInfo> allItems() {
|
||||
return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getContents();
|
||||
return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getAppContents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ class FolderIconLoadTest {
|
||||
val collections = modelHelper.getBgDataModel().collections
|
||||
|
||||
assertThat(collections.size()).isEqualTo(1)
|
||||
assertThat(collections.valueAt(0).contents.size).isEqualTo(itemCount)
|
||||
return collections.valueAt(0).contents
|
||||
assertThat(collections.valueAt(0).getAppContents().size).isEqualTo(itemCount)
|
||||
return collections.valueAt(0).getAppContents()
|
||||
}
|
||||
|
||||
private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) {
|
||||
|
||||
@@ -139,9 +139,9 @@ class ItemInflaterTest {
|
||||
@Test
|
||||
fun test_folder_inflated_on_UI() {
|
||||
val itemInfo = FolderInfo()
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
|
||||
val view =
|
||||
MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
|
||||
@@ -155,9 +155,9 @@ class ItemInflaterTest {
|
||||
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
|
||||
|
||||
val itemInfo = FolderInfo()
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
|
||||
val view =
|
||||
VIEW_PREINFLATION_EXECUTOR.submit(
|
||||
@@ -173,8 +173,8 @@ class ItemInflaterTest {
|
||||
fun test_app_pair_inflated_on_UI() {
|
||||
val itemInfo = AppPairInfo()
|
||||
itemInfo.itemType = ITEM_TYPE_APP_PAIR
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
|
||||
val view =
|
||||
MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
|
||||
@@ -189,8 +189,8 @@ class ItemInflaterTest {
|
||||
|
||||
val itemInfo = AppPairInfo()
|
||||
itemInfo.itemType = ITEM_TYPE_APP_PAIR
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.contents.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
itemInfo.add(workspaceItemInfo())
|
||||
|
||||
val view =
|
||||
VIEW_PREINFLATION_EXECUTOR.submit(
|
||||
|
||||
@@ -398,8 +398,9 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
|
||||
return false;
|
||||
}
|
||||
if (!mLauncher.isAppPairsEnabled() && task.isTaskSplit()) {
|
||||
// Overview actions aren't visible for split screen tasks.
|
||||
if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
|
||||
// Overview actions aren't visible for split screen tasks, except for save app pair
|
||||
// button on tablets.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user