Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 739b3c9f authored by Himanshu Gupta's avatar Himanshu Gupta
Browse files

Enabling Private Space Container in Launcher.

This CL adds the following:
1. Adds filtering and addition of Private Profile apps in main user
all apps recycler view
2. Enables decoration of Private Profile apps
3. Enables hiding Private Space container based upon a settings entry.

Flag: ACONFIG com.android.launcher3.Flags.enable_private_space DEVELOPMENT
Bug: 289223923
Test: Ran Launcher3 tests
Change-Id: I33dc55a3a39e75d3fc336ca6a488b282e2dd322c
parent 8ed8d67d
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVA
import static com.android.launcher3.model.LoaderTask.SMARTSPACE_ON_HOME_SCREEN;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;

import android.content.ComponentName;
import android.content.Context;
@@ -166,6 +167,8 @@ public class LauncherAppState implements SafeCloseable {
        onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
        mOnTerminateCallback.add(() ->
                settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister));
        // Register an observer to notify Launcher about Private Space settings toggle.
        registerPrivateSpaceHideWhenLockListener(settingsCache);
    }

    public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
@@ -188,6 +191,18 @@ public class LauncherAppState implements SafeCloseable {
        }
    }

    private void registerPrivateSpaceHideWhenLockListener(SettingsCache settingsCache) {
        SettingsCache.OnChangeListener psHideWhenLockChangedListener =
                this::onPrivateSpaceHideWhenLockChanged;
        settingsCache.register(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, psHideWhenLockChangedListener);
        mOnTerminateCallback.add(() -> settingsCache.unregister(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI,
                psHideWhenLockChangedListener));
    }

    private void onPrivateSpaceHideWhenLockChanged(boolean isPrivateSpaceHideOnLockEnabled) {
        mModel.forceReload();
    }

    private void refreshAndReloadLauncher() {
        LauncherIcons.clearPool();
        mIconCache.updateIconParams(
+35 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps;

import static com.android.launcher3.Flags.enableExpandingPauseWorkButton;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
@@ -69,6 +70,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
@@ -95,6 +97,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * All apps container view with search support for use in a dragging activity.
@@ -133,6 +136,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
    protected final Predicate<ItemInfo> mPersonalMatcher = ItemInfoMatcher.ofUser(
            Process.myUserHandle());
    protected WorkProfileManager mWorkManager;
    protected final PrivateProfileManager mPrivateProfileManager;
    protected final Point mFastScrollerOffset = new Point();
    protected final int mScrimColor;
    protected final float mHeaderThreshold;
@@ -175,6 +179,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
    protected SearchAdapterProvider<?> mMainAdapterProvider;
    private View mBottomSheetHandleArea;
    private boolean mHasWorkApps;
    private boolean mHasPrivateApps;
    private float[] mBottomSheetCornerRadii;
    private ScrimView mScrimView;
    private int mHeaderColor;
@@ -184,6 +189,8 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
    private int mTabsProtectionAlpha;
    @Nullable private AllAppsTransitionController mAllAppsTransitionController;

    private PrivateSpaceHeaderViewController mPrivateSpaceHeaderViewController;

    public ActivityAllAppsContainerView(Context context) {
        this(context, null);
    }
@@ -207,6 +214,11 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
                this,
                mActivityContext.getStatsLogManager(),
                UserCache.INSTANCE.get(mActivityContext));
        mPrivateProfileManager = new PrivateProfileManager(
                mActivityContext.getSystemService(UserManager.class),
                this,
                mActivityContext.getStatsLogManager(),
                UserCache.INSTANCE.get(mActivityContext));
        mAH = Arrays.asList(null, null, null);
        mNavBarScrimPaint = new Paint();
        mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
@@ -246,13 +258,20 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
     */
    protected void initContent() {
        mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
        if (Flags.enablePrivateSpace()) {
            mPrivateSpaceHeaderViewController =
                    new PrivateSpaceHeaderViewController(mPrivateProfileManager);
        }

        mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
                new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, null)));
                new AlphabeticalAppsList<>(mActivityContext,
                        mAllAppsStore,
                        null,
                        mPrivateProfileManager)));
        mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK,
                new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, mWorkManager)));
                new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, mWorkManager, null)));
        mAH.set(SEARCH, new AdapterHolder(SEARCH,
                new AlphabeticalAppsList<>(mActivityContext, null, null)));
                new AlphabeticalAppsList<>(mActivityContext, null, null, null)));

        getLayoutInflater().inflate(R.layout.all_apps_content, this);
        mHeader = findViewById(R.id.all_apps_header);
@@ -592,7 +611,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
        } else {
            mainRecyclerView = findViewById(R.id.apps_list_view);
            workRecyclerView = null;
            mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, null);
            mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, mPersonalMatcher);
            mAH.get(AdapterHolder.WORK).mRecyclerView = null;
        }
        setUpCustomRecyclerViewPool(
@@ -860,7 +879,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>

    protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList) {
        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
                mMainAdapterProvider);
                mMainAdapterProvider, mPrivateSpaceHeaderViewController);
    }

    // TODO(b/216683257): Remove when Taskbar All Apps supports search.
@@ -973,13 +992,19 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>

    @VisibleForTesting
    public void onAppsUpdated() {
        mHasWorkApps = mWorkManager.hasWorkApps();
        mHasWorkApps = Stream.of(mAllAppsStore.getApps())
                .anyMatch(mWorkManager.getItemInfoMatcher());
        mHasPrivateApps = Stream.of(mAllAppsStore.getApps())
                .anyMatch(mPrivateProfileManager.getItemInfoMatcher());
        if (!isSearching()) {
            rebindAdapters();
        }
        if (mHasWorkApps) {
            mWorkManager.reset();
        }
        if (mHasPrivateApps) {
            mPrivateProfileManager.reset();
        }

        mActivityContext.getStatsLogManager().logger()
                .withCardinality(mAllAppsStore.getApps().length)
@@ -1245,6 +1270,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
        return mAH.get(SEARCH).mAppsList;
    }

    public AlphabeticalAppsList<T> getPersonalAppList() {
        return mAH.get(MAIN).mAppsList;
    }

    public FloatingHeaderView getFloatingHeaderView() {
        return mHeader;
    }
+3 −2
Original line number Diff line number Diff line
@@ -73,8 +73,9 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends


    public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
            AlphabeticalAppsList apps, SearchAdapterProvider<?> adapterProvider) {
        super(activityContext, inflater, apps, adapterProvider);
            AlphabeticalAppsList apps, SearchAdapterProvider<?> adapterProvider,
            PrivateSpaceHeaderViewController privateSpaceHeaderViewController) {
        super(activityContext, inflater, apps, adapterProvider, privateSpaceHeaderViewController);
        mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
        mGridLayoutMgr.setSpanSizeLookup(new GridSpanSizer());
        setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
+56 −15
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;

import com.android.launcher3.Flags;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -47,6 +48,8 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement

    private final WorkProfileManager mWorkProviderManager;

    private final PrivateProfileManager mPrivateProviderManager;

    /**
     * Info about a fast scroller section, depending if sections are merged, the fast scroller
     * sections will not be the same set as the section headers.
@@ -68,6 +71,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement

    // The set of apps from the system
    private final List<AppInfo> mApps = new ArrayList<>();
    private final List<AppInfo> mPrivateApps = new ArrayList<>();
    @Nullable
    private final AllAppsStore<T> mAllAppsStore;

@@ -87,11 +91,12 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
    private Predicate<ItemInfo> mItemFilter;

    public AlphabeticalAppsList(Context context, @Nullable AllAppsStore<T> appsStore,
            WorkProfileManager workProfileManager) {
            WorkProfileManager workProfileManager, PrivateProfileManager privateProfileManager) {
        mAllAppsStore = appsStore;
        mActivityContext = ActivityContext.lookupContext(context);
        mAppNameComparator = new AppInfoComparator(context);
        mWorkProviderManager = workProfileManager;
        mPrivateProviderManager = privateProfileManager;
        mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
        if (mAllAppsStore != null) {
            mAllAppsStore.addUpdateListener(this);
@@ -197,12 +202,20 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
        }
        // Sort the list of apps
        mApps.clear();
        mPrivateApps.clear();

        Stream<AppInfo> appSteam = Stream.of(mAllAppsStore.getApps());
        Stream<AppInfo> privateAppStream = Stream.of(mAllAppsStore.getApps());

        if (!hasSearchResults() && mItemFilter != null) {
            appSteam = appSteam.filter(mItemFilter);
            if (mPrivateProviderManager != null) {
                privateAppStream = privateAppStream
                        .filter(mPrivateProviderManager.getItemInfoMatcher());
            }
        }
        appSteam = appSteam.sorted(mAppNameComparator);
        privateAppStream = privateAppStream.sorted(mAppNameComparator);

        // As a special case for some languages (currently only Simplified Chinese), we may need to
        // coalesce sections
@@ -221,6 +234,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
        }

        appSteam.forEachOrdered(mApps::add);
        privateAppStream.forEachOrdered(mPrivateApps::add);
        // Recompose the set of adapter items from the current set of apps
        if (mSearchResults.isEmpty()) {
            updateAdapterItems();
@@ -250,18 +264,10 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
                addApps = mWorkProviderManager.shouldShowWorkApps();
            }
            if (addApps) {
                String lastSectionName = null;
                for (AppInfo info : mApps) {
                    mAdapterItems.add(AdapterItem.asApp(info));

                    String sectionName = info.sectionName;
                    // Create a new section if the section names do not match
                    if (!sectionName.equals(lastSectionName)) {
                        lastSectionName = sectionName;
                        mFastScrollerSections.add(new FastScrollSectionInfo(sectionName, position));
                    }
                    position++;
                addAppsWithSections(mApps, position);
            }
            if (Flags.enablePrivateSpace()) {
                addPrivateSpaceItems(position);
            }
        }
        mAccessibilityResultsCount = (int) mAdapterItems.stream()
@@ -275,7 +281,8 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
            int rowIndex = -1;
            for (AdapterItem item : mAdapterItems) {
                item.rowIndex = 0;
                if (BaseAllAppsAdapter.isDividerViewType(item.viewType)) {
                if (BaseAllAppsAdapter.isDividerViewType(item.viewType)
                        || BaseAllAppsAdapter.isPrivateSpaceHeaderView(item.viewType)) {
                    numAppsInSection = 0;
                } else if (BaseAllAppsAdapter.isIconViewType(item.viewType)) {
                    if (numAppsInSection % mNumAppsPerRowAllApps == 0) {
@@ -297,6 +304,40 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
        }
    }

    void addPrivateSpaceItems(int position) {
        if (mPrivateProviderManager != null
                && !mPrivateProviderManager.isPrivateSpaceHidden()
                && !mPrivateApps.isEmpty()) {
            // Always add PS Header if Space is present and visible.
            position += mPrivateProviderManager.addPrivateSpaceHeader(mAdapterItems);
            int privateSpaceState = mPrivateProviderManager.getCurrentState();
            switch (privateSpaceState) {
                case PrivateProfileManager.STATE_DISABLED:
                case PrivateProfileManager.STATE_TRANSITION:
                    break;
                case PrivateProfileManager.STATE_ENABLED:
                    // Add PS Apps only in Enabled State.
                    addAppsWithSections(mPrivateApps, position);
                    break;
            }
        }
    }

    private void addAppsWithSections(List<AppInfo> appList, int startPosition) {
        String lastSectionName = null;
        for (AppInfo info : appList) {
            mAdapterItems.add(AdapterItem.asApp(info));

            String sectionName = info.sectionName;
            // Create a new section if the section names do not match
            if (!sectionName.equals(lastSectionName)) {
                lastSectionName = sectionName;
                mFastScrollerSections.add(new FastScrollSectionInfo(sectionName, startPosition));
            }
            startPosition++;
        }
    }

    private static class MyDiffCallback extends DiffUtil.Callback {

        private final List<AdapterItem> mOldList;
+20 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;
@@ -139,9 +140,16 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
    protected final OnClickListener mOnIconClickListener;
    protected final OnLongClickListener mOnIconLongClickListener;
    protected OnFocusChangeListener mIconFocusListener;
    private final PrivateSpaceHeaderViewController mPrivateSpaceHeaderViewController;

    public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
            AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) {
        this(activityContext, inflater, apps, adapterProvider, null);
    }

    public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
            AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider,
            PrivateSpaceHeaderViewController privateSpaceHeaderViewController) {
        mActivityContext = activityContext;
        mApps = apps;
        mLayoutInflater = inflater;
@@ -150,6 +158,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
        mOnIconLongClickListener = mActivityContext.getAllAppsItemLongClickListener();

        mAdapterProvider = adapterProvider;
        mPrivateSpaceHeaderViewController = privateSpaceHeaderViewController;
    }

    /** Checks if the passed viewType represents all apps divider. */
@@ -162,6 +171,11 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
        return isViewType(viewType, VIEW_TYPE_MASK_ICON);
    }

    /** Checks if the passed viewType represents private space header. */
    public static boolean isPrivateSpaceHeaderView(int viewType) {
        return isViewType(viewType, VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER);
    }

    public void setIconFocusListener(OnFocusChangeListener focusListener) {
        mIconFocusListener = focusListener;
    }
@@ -230,6 +244,12 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
                break;
            }
            case VIEW_TYPE_PRIVATE_SPACE_HEADER:
                RelativeLayout psHeaderLayout = holder.itemView.findViewById(
                        R.id.ps_header_layout);
                assert mPrivateSpaceHeaderViewController != null;
                assert psHeaderLayout != null;
                mPrivateSpaceHeaderViewController.addPrivateSpaceHeaderViewElements(psHeaderLayout);
                break;
            case VIEW_TYPE_ALL_APPS_DIVIDER:
            case VIEW_TYPE_WORK_DISABLED_CARD:
                // nothing to do
Loading