chore: Bump submodule
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.data
|
||||
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.util.putAll
|
||||
import androidx.core.util.valueIterator
|
||||
import app.lawnchair.preferences2.PreferenceManager2
|
||||
import com.android.launcher3.BuildConfig
|
||||
import com.android.launcher3.BuildConfigs
|
||||
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
import com.android.launcher3.Workspace
|
||||
import com.android.launcher3.model.data.WorkspaceChangeEvent.AddEvent
|
||||
import com.android.launcher3.model.data.WorkspaceChangeEvent.RemoveEvent
|
||||
import com.android.launcher3.model.data.WorkspaceChangeEvent.UpdateEvent
|
||||
import com.android.launcher3.util.IntArray
|
||||
import com.android.launcher3.util.IntSet
|
||||
import com.android.launcher3.util.ItemInfoMatcher
|
||||
import com.patrykmichalik.opto.core.firstBlocking
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
|
||||
/**
|
||||
* An immutable representation of all the workspace items (shortcuts, folders, widgets and predicted
|
||||
* items)
|
||||
*/
|
||||
sealed class WorkspaceData : Iterable<ItemInfo> {
|
||||
|
||||
/** Creates an array of valid workspace screens based on current items in the model. */
|
||||
// LC-Note: Add context to replace QSB_ON_FIRST_SCREEN config
|
||||
fun collectWorkspaceScreens(context: Context): IntArray {
|
||||
val prefs2 = PreferenceManager2.INSTANCE.get(context)
|
||||
val smartspaceEnabled = prefs2.enableSmartspace.firstBlocking()
|
||||
|
||||
val screenSet = IntSet()
|
||||
forEach { if (it.container == CONTAINER_DESKTOP) screenSet.add(it.screenId) }
|
||||
if (smartspaceEnabled || screenSet.isEmpty) {
|
||||
screenSet.add(Workspace.FIRST_SCREEN_ID)
|
||||
}
|
||||
return screenSet.array
|
||||
}
|
||||
|
||||
/** Returns the [ItemInfo] associated with the [id] or null */
|
||||
abstract operator fun get(id: Int): ItemInfo?
|
||||
|
||||
fun stream(): Stream<ItemInfo> = StreamSupport.stream(spliterator(), false)
|
||||
|
||||
/** Version determines the uniqueness per model load cycle */
|
||||
abstract val version: Int
|
||||
/** Number of times, this data has been modified */
|
||||
internal abstract val modificationId: Int
|
||||
/** Previous modifications to this data in reverse order: 1st entry is the latest update */
|
||||
protected abstract val changeHistory: List<WorkspaceChangeEvent>
|
||||
|
||||
/**
|
||||
* Returns the predicted items for the provided [containerId] or an empty list id no such
|
||||
* container exists
|
||||
*/
|
||||
fun getPredictedContents(containerId: Int): List<ItemInfo> =
|
||||
get(containerId).let { if (it is PredictedContainerInfo) it.getContents() else null }
|
||||
?: emptyList()
|
||||
|
||||
/** Returns an immutable copy of the dataset */
|
||||
abstract fun copy(): WorkspaceData
|
||||
|
||||
/**
|
||||
* Returns a list of [WorkspaceChangeEvent] to apply for reaching from [source] to current
|
||||
* state. Returns an empty list of the source is same as the current state and null if such a
|
||||
* transition is not possible.
|
||||
*/
|
||||
fun diff(source: WorkspaceData): List<WorkspaceChangeEvent>? {
|
||||
if (version != source.version) return null
|
||||
val requiredHistorySize = modificationId - source.modificationId
|
||||
|
||||
val changes = changeHistory
|
||||
return when {
|
||||
requiredHistorySize < 0 -> null
|
||||
requiredHistorySize > changes.size -> null
|
||||
requiredHistorySize == 0 -> emptyList()
|
||||
else -> changes.subList(0, requiredHistorySize).reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is WorkspaceData &&
|
||||
other.version == version &&
|
||||
other.modificationId == modificationId
|
||||
}
|
||||
|
||||
/** A mutable implementation of [WorkspaceData] */
|
||||
class MutableWorkspaceData : WorkspaceData() {
|
||||
|
||||
private val itemsIdMap = SparseArray<ItemInfo>()
|
||||
|
||||
override var version: Int = VERSION_COUNTER.incrementAndGet()
|
||||
|
||||
override var modificationId: Int = 0
|
||||
|
||||
override val changeHistory = mutableListOf<WorkspaceChangeEvent>()
|
||||
|
||||
override fun iterator() = itemsIdMap.valueIterator()
|
||||
|
||||
override fun get(id: Int): ItemInfo? = itemsIdMap.get(id)
|
||||
|
||||
/** Replaces the existing dataset with [items] */
|
||||
fun replaceDataMap(items: SparseArray<ItemInfo>) {
|
||||
itemsIdMap.clear()
|
||||
itemsIdMap.putAll(items)
|
||||
version = VERSION_COUNTER.incrementAndGet()
|
||||
modificationId = 0
|
||||
changeHistory.clear()
|
||||
}
|
||||
|
||||
/** Adds the [item] to the dataset */
|
||||
fun addItems(items: List<ItemInfo>, owner: Any?) {
|
||||
items.forEach { itemsIdMap[it.id] = it }
|
||||
pushUpdate(AddEvent(items, owner))
|
||||
}
|
||||
|
||||
/** Removes existing [items] from the dataset */
|
||||
fun removeItems(items: Collection<ItemInfo>, owner: Any?) {
|
||||
items.forEach { itemsIdMap.remove(it.id) }
|
||||
pushUpdate(RemoveEvent(ItemInfoMatcher.ofItems(items), owner))
|
||||
}
|
||||
|
||||
/** Replaces an existing [item] from the dataset */
|
||||
fun replaceItem(item: ItemInfo, owner: Any?) {
|
||||
itemsIdMap[item.id] = item
|
||||
notifyItemsUpdated(listOf(item), owner)
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this dataset that or updates already performed on existing [items]. Since the
|
||||
* underlying [ItemInfo]s are mutable objects, its possible to update their properties
|
||||
* without going though this dataset
|
||||
*/
|
||||
fun notifyItemsUpdated(items: List<ItemInfo>, owner: Any?) {
|
||||
pushUpdate(UpdateEvent(items, owner))
|
||||
}
|
||||
|
||||
private fun pushUpdate(update: WorkspaceChangeEvent) {
|
||||
modificationId++
|
||||
changeHistory.add(0, update)
|
||||
if (changeHistory.size > MAX_HISTORY_SIZE) changeHistory.removeAt(changeHistory.lastIndex)
|
||||
}
|
||||
|
||||
override fun copy(): WorkspaceData =
|
||||
ImmutableWorkspaceData(version, modificationId, changeHistory.toList(), itemsIdMap)
|
||||
}
|
||||
|
||||
/** An immutable implementation of [WorkspaceData] */
|
||||
class ImmutableWorkspaceData(
|
||||
override val version: Int,
|
||||
override val modificationId: Int,
|
||||
override val changeHistory: List<WorkspaceChangeEvent>,
|
||||
items: SparseArray<ItemInfo>,
|
||||
) : WorkspaceData() {
|
||||
|
||||
private val itemsIdMap = items.clone()
|
||||
|
||||
override fun iterator() = itemsIdMap.valueIterator()
|
||||
|
||||
override fun get(id: Int): ItemInfo? = itemsIdMap.get(id)
|
||||
|
||||
override fun copy(): WorkspaceData = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Maximum number of historic change events to keep in memory
|
||||
@VisibleForTesting const val MAX_HISTORY_SIZE = 4
|
||||
|
||||
private val VERSION_COUNTER = AtomicInteger()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.preview;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_PREVIEW_RENDERER;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
|
||||
|
||||
import static java.util.Comparator.comparingDouble;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextClock;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import app.lawnchair.preferences2.PreferenceManager2;
|
||||
import com.android.launcher3.BuildConfig;
|
||||
import com.android.launcher3.BuildConfigs;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Hotseat;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.WorkspaceLayoutManager;
|
||||
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
|
||||
import com.android.launcher3.celllayout.CellPosMapper;
|
||||
import com.android.launcher3.dagger.LauncherComponentProvider;
|
||||
import com.android.launcher3.dragndrop.SimpleDragLayer;
|
||||
import com.android.launcher3.graphics.FragmentWithPreview;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceData;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.util.BaseContext;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.ItemInflater;
|
||||
import com.android.launcher3.util.LauncherBindableItemsContainer;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.LauncherWidgetHolder;
|
||||
|
||||
import com.patrykmichalik.opto.core.PreferenceExtensionsKt;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
|
||||
* Steps:
|
||||
* 1) Create a dummy icon info with just white icon
|
||||
* 2) Inflate a strip down layout definition for Launcher
|
||||
* 3) Place appropriate elements like icons and first-page qsb
|
||||
* 4) Measure and draw the view on a canvas
|
||||
*/
|
||||
public class LauncherPreviewRenderer extends BaseContext
|
||||
implements WorkspaceLayoutManager, LayoutInflater.Factory2,
|
||||
LauncherBindableItemsContainer, BgDataModel.Callbacks {
|
||||
|
||||
public final CompletableFuture<View> initialRender = new CompletableFuture<>();
|
||||
|
||||
private final Handler mUiHandler;
|
||||
private final InvariantDeviceProfile mIdp;
|
||||
private final DeviceProfile mDp;
|
||||
private final LayoutInflater mHomeElementInflater;
|
||||
private final LauncherPreviewLayout mRootView;
|
||||
private final Hotseat mHotseat;
|
||||
private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>();
|
||||
private final ItemInflater<LauncherPreviewRenderer> mItemInflater;
|
||||
|
||||
private final PreferenceManager2 mPreferenceManager2;
|
||||
|
||||
public LauncherPreviewRenderer(Context context,
|
||||
int workspaceScreenId,
|
||||
@Nullable SparseIntArray wallpaperColorResources,
|
||||
LauncherModel model,
|
||||
int themeRes) {
|
||||
|
||||
super(context, themeRes);
|
||||
mPreferenceManager2 = PreferenceManager2.getInstance(context);
|
||||
|
||||
mUiHandler = new Handler(Looper.getMainLooper());
|
||||
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
|
||||
mDp = getDeviceProfileForPreview(context).toBuilder(context)
|
||||
.setViewScaleProvider(new PreviewScaleProvider(this)).build();
|
||||
Rect insets = getInsets(context);
|
||||
mDp.updateInsets(insets);
|
||||
|
||||
mHomeElementInflater = LayoutInflater.from(
|
||||
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
|
||||
mHomeElementInflater.setFactory2(this);
|
||||
|
||||
int layoutRes = mDp.getDeviceProperties().isTwoPanels()
|
||||
? R.layout.launcher_preview_two_panel_layout
|
||||
: R.layout.launcher_preview_layout;
|
||||
mRootView = (LauncherPreviewLayout) mHomeElementInflater.inflate(
|
||||
layoutRes, null, false);
|
||||
mRootView.setInsets(insets);
|
||||
measureAndLayoutRootView();
|
||||
|
||||
mHotseat = mRootView.findViewById(R.id.hotseat);
|
||||
mHotseat.resetLayout(false);
|
||||
|
||||
CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
|
||||
firstScreen.setPadding(
|
||||
mDp.workspacePadding.left + mDp.cellLayoutPaddingPx.left,
|
||||
mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
|
||||
mDp.getDeviceProperties().isTwoPanels() ? (mDp.cellLayoutBorderSpacePx.x / 2)
|
||||
: (mDp.workspacePadding.right + mDp.cellLayoutPaddingPx.right),
|
||||
mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
|
||||
);
|
||||
|
||||
if (mDp.getDeviceProperties().isTwoPanels()) {
|
||||
CellLayout rightPanel = mRootView.findViewById(R.id.workspace_right);
|
||||
rightPanel.setPadding(
|
||||
mDp.cellLayoutBorderSpacePx.x / 2,
|
||||
mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
|
||||
mDp.workspacePadding.right + mDp.cellLayoutPaddingPx.right,
|
||||
mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
|
||||
);
|
||||
|
||||
int closestEvenPageId = workspaceScreenId - (workspaceScreenId % 2);
|
||||
mWorkspaceScreens.put(closestEvenPageId, firstScreen);
|
||||
mWorkspaceScreens.put(closestEvenPageId + 1, rightPanel);
|
||||
} else {
|
||||
mWorkspaceScreens.put(workspaceScreenId, firstScreen);
|
||||
}
|
||||
|
||||
LauncherWidgetHolder widgetHolder = LauncherComponentProvider.get(this)
|
||||
.getWidgetHolderFactory().newInstance(this);
|
||||
if (wallpaperColorResources != null) {
|
||||
widgetHolder.setOnViewCreationCallback(
|
||||
v -> v.setColorResources(wallpaperColorResources));
|
||||
}
|
||||
|
||||
mItemInflater = new ItemInflater<>(
|
||||
this,
|
||||
widgetHolder,
|
||||
view -> { },
|
||||
(view, b) -> { },
|
||||
mHotseat
|
||||
);
|
||||
onViewCreated();
|
||||
model.addCallbacksAndLoad(this);
|
||||
getLifecycle().addObserver(new DefaultLifecycleObserver() {
|
||||
@Override
|
||||
public void onDestroy(@NonNull LifecycleOwner owner) {
|
||||
model.removeCallbacks(LauncherPreviewRenderer.this);
|
||||
widgetHolder.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsettableFrameLayout getRootView() {
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public LauncherBindableItemsContainer getContent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseDragLayer getDragLayer() {
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceProfile getDeviceProfile() {
|
||||
return mDp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hotseat getHotseat() {
|
||||
return mHotseat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device profile based on resource configuration for previewing various display
|
||||
* sizes
|
||||
*/
|
||||
private DeviceProfile getDeviceProfileForPreview(Context context) {
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
|
||||
return mIdp.getBestMatch(
|
||||
config.screenWidthDp * density,
|
||||
config.screenHeightDp * density,
|
||||
WindowManagerProxy.INSTANCE.get(context).getRotation(context)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
|
||||
if ("TextClock".equals(name)) {
|
||||
// Workaround for TextClock accessing handler for unregistering ticker.
|
||||
return new TextClock(context, attrs) {
|
||||
|
||||
@Override
|
||||
public Handler getHandler() {
|
||||
return mUiHandler;
|
||||
}
|
||||
};
|
||||
} else if (!"fragment".equals(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
|
||||
FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
|
||||
context, ta.getString(R.styleable.PreviewFragment_android_name));
|
||||
f.enterPreviewMode(context);
|
||||
f.onInit(null);
|
||||
|
||||
View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
|
||||
view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
|
||||
ta.recycle();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(String name, Context context, AttributeSet attrs) {
|
||||
return onCreateView(null, name, context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the components in the bottom row.
|
||||
*
|
||||
* @param hide True to hide and false to show.
|
||||
*/
|
||||
@UiThread
|
||||
public void hideBottomRow(boolean hide) {
|
||||
if (mDp.isTaskbarPresent) {
|
||||
// hotseat icons on bottom
|
||||
mHotseat.setIconsAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
|
||||
if (mDp.isQsbInline) {
|
||||
mHotseat.setQsbAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
|
||||
}
|
||||
} else {
|
||||
mHotseat.setQsbAlpha(hide ? 0 : 1, ALPHA_CHANNEL_PREVIEW_RENDERER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellLayout getScreenWithId(int screenId) {
|
||||
return mWorkspaceScreens.get(screenId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellPosMapper getCellPosMapper() {
|
||||
return CellPosMapper.DEFAULT;
|
||||
}
|
||||
|
||||
private List<CellLayout> getAllLayouts() {
|
||||
List<CellLayout> screens = new ArrayList<>(mWorkspaceScreens.values());
|
||||
screens.add(getHotseat());
|
||||
return screens;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View mapOverItems(@NonNull ItemOperator op) {
|
||||
return Workspace.mapOverCellLayouts(getAllLayouts().toArray(new CellLayout[0]), op);
|
||||
}
|
||||
|
||||
private void dispatchVisibilityAggregated(View view, boolean isVisible) {
|
||||
// Similar to View.dispatchVisibilityAggregated implementation.
|
||||
final boolean thisVisible = view.getVisibility() == VISIBLE;
|
||||
if (thisVisible || !isVisible) {
|
||||
view.onVisibilityAggregated(isVisible);
|
||||
}
|
||||
|
||||
if (view instanceof ViewGroup vg) {
|
||||
isVisible = thisVisible && isVisible;
|
||||
int count = vg.getChildCount();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindCompleteModel(@NonNull WorkspaceData itemIdMap, boolean isBindingSync) {
|
||||
getAllLayouts().forEach(CellLayout::removeAllViews);
|
||||
|
||||
// Separate the items that are on the current screen, and the other remaining items.
|
||||
itemIdMap.stream()
|
||||
.filter(currentScreenContentFilter(IntSet.wrap(mWorkspaceScreens.keySet())))
|
||||
.forEach(this::inflateAndAdd);
|
||||
populateHotseatPredictions(itemIdMap);
|
||||
|
||||
// Add first page QSB
|
||||
if (PreferenceExtensionsKt.firstBlocking(mPreferenceManager2.getEnableSmartspace())) {
|
||||
CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
|
||||
if (firstScreen != null) {
|
||||
View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false);
|
||||
// TODO: set bgHandler on qsb when it is BaseTemplateCard, which requires API
|
||||
// changes.
|
||||
CellLayoutLayoutParams lp = new CellLayoutLayoutParams(
|
||||
0, 0, firstScreen.getCountX(), 1);
|
||||
lp.canReorder = false;
|
||||
firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
|
||||
}
|
||||
}
|
||||
measureAndLayoutRootView();
|
||||
dispatchVisibilityAggregated(mRootView, true);
|
||||
measureAndLayoutRootView();
|
||||
// Additional measure for views which use auto text size API
|
||||
measureAndLayoutRootView();
|
||||
initialRender.complete(mRootView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindItemsUpdated(@NonNull Set<ItemInfo> updates) {
|
||||
updateContainerItems(updates, this);
|
||||
}
|
||||
|
||||
private void populateHotseatPredictions(WorkspaceData itemIdMap) {
|
||||
List<ItemInfo> predictions = itemIdMap.getPredictedContents(CONTAINER_HOTSEAT_PREDICTION);
|
||||
int predictionIndex = 0;
|
||||
for (int rank = 0; rank < mDp.numShownHotseatIcons; rank++) {
|
||||
if (predictions.size() <= predictionIndex) continue;
|
||||
|
||||
int cellX = mHotseat.getCellXFromOrder(rank);
|
||||
int cellY = mHotseat.getCellYFromOrder(rank);
|
||||
if (mHotseat.isOccupied(cellX, cellY)) continue;
|
||||
|
||||
WorkspaceItemInfo itemInfo =
|
||||
new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(predictionIndex));
|
||||
predictionIndex++;
|
||||
itemInfo.rank = rank;
|
||||
itemInfo.cellX = cellX;
|
||||
itemInfo.cellY = cellY;
|
||||
itemInfo.screenId = rank;
|
||||
inflateAndAdd(itemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void inflateAndAdd(ItemInfo itemInfo) {
|
||||
View view = mItemInflater.inflateItem(itemInfo);
|
||||
if (view != null) {
|
||||
addInScreenFromBind(view, itemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void measureAndLayoutRootView() {
|
||||
int width = mDp.getDeviceProperties().getWidthPx();
|
||||
int height = mDp.getDeviceProperties().getHeightPx();
|
||||
mRootView.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
|
||||
mRootView.layout(0, 0, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the insets of the screen closest to the display given by the context
|
||||
*/
|
||||
private static Rect getInsets(Context context) {
|
||||
Display display = context.getDisplay();
|
||||
return DisplayController.INSTANCE.get(context).getInfo().supportedBounds.stream()
|
||||
.filter(w -> w.rotationHint == display.getRotation())
|
||||
.min(comparingDouble(w ->
|
||||
Math.pow(display.getWidth() - w.availableSize.x, 2)
|
||||
+ Math.pow(display.getHeight() - w.availableSize.y, 2)))
|
||||
.map(w -> new Rect(w.insets))
|
||||
.orElse(new Rect());
|
||||
}
|
||||
|
||||
/** Root layout for launcher preview that intercepts all touch events. */
|
||||
public static class LauncherPreviewLayout extends SimpleDragLayer<LauncherPreviewRenderer> {
|
||||
public LauncherPreviewLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user