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

Commit 38a6fce5 authored by arangelov's avatar arangelov
Browse files

Add infrastructure for sharesheet work tab (part 1)

This CL consists of the following changes:
- Create a ViewPager to show work and personal pages
- Create a custom WrapHeightViewPager to accommodate
the behaviour of the existing intent resolver
- Create PagerAdapters to use in the ViewPager for
both intent resolver and chooser
- Modify ResolverListController to work with userIds

Test: manually performed text and picture sharing on a 1-user device
Test: manually performed text and picture sharing on a device with
work profile on both profiles
Test: manually compared UX between a device with my changes and a
previous version of the share sheet
Test: atest com.android.internal.app.ResolverActivityTest
Test: atest com.android.internal.app.ChooserActivityTest
Test: atest com.android.internal.app.ResolverListControllerTest
Bug: 142537267
Bug: 142538125
Change-Id: I4487b86e2a7566a3e14b36d3e0eb13a30d00349c
parent 3ee1b172
Loading
Loading
Loading
Loading
+151 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.internal.app;
import android.annotation.IntDef;
import android.content.Context;
import android.os.UserHandle;
import android.view.View;
import android.view.ViewGroup;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.PagerAdapter;

import com.android.internal.util.Preconditions;
import com.android.internal.widget.ViewPager;

/**
 * Skeletal {@link PagerAdapter} implementation of a work or personal profile page for
 * intent resolution (including share sheet).
 */
public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {

    static final int PROFILE_PERSONAL = 0;
    static final int PROFILE_WORK = 1;
    @IntDef({PROFILE_PERSONAL, PROFILE_WORK})
    @interface Profile {}

    private final Context mContext;
    private int mCurrentPage;

    AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
        mContext = Preconditions.checkNotNull(context);
        mCurrentPage = currentPage;
    }

    Context getContext() {
        return mContext;
    }

    /**
     * Sets this instance of this class as {@link ViewPager}'s {@link PagerAdapter} and sets
     * an {@link ViewPager.OnPageChangeListener} where it keeps track of the currently displayed
     * page and rebuilds the list.
     */
    void setupViewPager(ViewPager viewPager) {
        viewPager.setCurrentItem(mCurrentPage);
        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                mCurrentPage = position;
                getCurrentListAdapter().rebuildList();
            }
        });
        viewPager.setAdapter(this);
    }

    @Override
    public ViewGroup instantiateItem(ViewGroup container, int position) {
        final ProfileDescriptor profileDescriptor = getItem(position);
        setupListAdapter(position);
        container.addView(profileDescriptor.rootView);
        return profileDescriptor.rootView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        container.removeView((View) view);
    }

    @Override
    public int getCount() {
        return getItemCount();
    }

    protected int getCurrentPage() {
        return mCurrentPage;
    }

    UserHandle getCurrentUserHandle() {
        return getCurrentListAdapter().mResolverListController.getUserHandle();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return null;
    }

    /**
     * Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>.
     * <ul>
     * <li>For a device with only one user, <code>pageIndex</code> value of
     * <code>0</code> would return the personal profile {@link ProfileDescriptor}.</li>
     * <li>For a device with a work profile, <code>pageIndex</code> value of <code>0</code> would
     * return the personal profile {@link ProfileDescriptor}, and <code>pageIndex</code> value of
     * <code>1</code> would return the work profile {@link ProfileDescriptor}.</li>
     * </ul>
     */
    abstract ProfileDescriptor getItem(int pageIndex);

    /**
     * Returns the number of {@link ProfileDescriptor} objects.
     * <p>For a normal consumer device with only one user returns <code>1</code>.
     * <p>For a device with a work profile returns <code>2</code>.
     */
    abstract int getItemCount();

    /**
     * Responsible for assigning an adapter to the list view for the relevant page, specified by
     * <code>pageIndex</code>, and other list view-related initialization procedures.
     */
    abstract void setupListAdapter(int pageIndex);

    /**
     * Returns the adapter of the list view for the relevant page specified by
     * <code>pageIndex</code>.
     * <p>This method is meant to be implemented with an implementation-specific return type
     * depending on the adapter type.
     */
    abstract Object getAdapterForIndex(int pageIndex);

    @VisibleForTesting
    public abstract ResolverListAdapter getCurrentListAdapter();

    abstract Object getCurrentRootAdapter();

    abstract ViewGroup getCurrentAdapterView();

    protected class ProfileDescriptor {
        final ViewGroup rootView;
        ProfileDescriptor(ViewGroup rootView) {
            this.rootView = rootView;
        }
    }
}
 No newline at end of file
+234 −121

File changed.

Preview size limit exceeded, changes collapsed.

+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.GridLayoutManager;
import com.android.internal.widget.PagerAdapter;
import com.android.internal.widget.RecyclerView;

/**
 * A {@link PagerAdapter} which describes the work and personal profile share sheet screens.
 */
@VisibleForTesting
public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
    private static final int SINGLE_CELL_SPAN_SIZE = 1;

    private final ChooserProfileDescriptor[] mItems;

    ChooserMultiProfilePagerAdapter(Context context,
            ChooserActivity.ChooserGridAdapter adapter) {
        super(context, /* currentPage */ 0);
        mItems = new ChooserProfileDescriptor[] {
                createProfileDescriptor(adapter)
        };
    }

    ChooserMultiProfilePagerAdapter(Context context,
            ChooserActivity.ChooserGridAdapter personalAdapter,
            ChooserActivity.ChooserGridAdapter workAdapter,
            @Profile int defaultProfile) {
        super(context, /* currentPage */ defaultProfile);
        mItems = new ChooserProfileDescriptor[] {
                createProfileDescriptor(personalAdapter),
                createProfileDescriptor(workAdapter)
        };
    }

    private ChooserProfileDescriptor createProfileDescriptor(
            ChooserActivity.ChooserGridAdapter adapter) {
        final LayoutInflater inflater = LayoutInflater.from(getContext());
        final ViewGroup rootView =
                (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false);
        return new ChooserProfileDescriptor(rootView, adapter);
    }

    RecyclerView getListViewForIndex(int index) {
        return getItem(index).recyclerView;
    }

    @Override
    ChooserProfileDescriptor getItem(int pageIndex) {
        return mItems[pageIndex];
    }

    @Override
    int getItemCount() {
        return mItems.length;
    }

    @Override
    ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
        return mItems[pageIndex].chooserGridAdapter;
    }

    @Override
    void setupListAdapter(int pageIndex) {
        final RecyclerView recyclerView = getItem(pageIndex).recyclerView;
        ChooserActivity.ChooserGridAdapter chooserGridAdapter =
                getItem(pageIndex).chooserGridAdapter;
        recyclerView.setAdapter(chooserGridAdapter);
        GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
        glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow());
        glm.setSpanSizeLookup(
                new GridLayoutManager.SpanSizeLookup() {
                    @Override
                    public int getSpanSize(int position) {
                        return chooserGridAdapter.shouldCellSpan(position)
                                ? SINGLE_CELL_SPAN_SIZE
                                : glm.getSpanCount();
                    }
                });
    }

    @Override
    @VisibleForTesting
    public ChooserListAdapter getCurrentListAdapter() {
        return getAdapterForIndex(getCurrentPage()).getListAdapter();
    }

    @Override
    ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() {
        return getAdapterForIndex(getCurrentPage());
    }

    @Override
    RecyclerView getCurrentAdapterView() {
        return getListViewForIndex(getCurrentPage());
    }

    class ChooserProfileDescriptor extends ProfileDescriptor {
        private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
        private RecyclerView recyclerView;
        ChooserProfileDescriptor(ViewGroup rootView, ChooserActivity.ChooserGridAdapter adapter) {
            super(rootView);
            chooserGridAdapter = adapter;
            recyclerView = rootView.findViewById(R.id.resolver_list);
        }
    }
}
+214 −95

File changed.

Preview size limit exceeded, changes collapsed.

+6 −3
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,7 +80,7 @@ public class ResolverListAdapter extends BaseAdapter {

    // This one is the list that the Adapter will actually present.
    List<DisplayResolveInfo> mDisplayList;
    List<ResolvedComponentInfo> mUnfilteredResolveList;
    private List<ResolvedComponentInfo> mUnfilteredResolveList;

    private int mLastChosenPosition = -1;
    private boolean mFilterLastUsed;
@@ -162,6 +161,10 @@ public class ResolverListAdapter extends BaseAdapter {
        mResolverListController.updateChooserCounts(packageName, userId, action);
    }

    List<ResolvedComponentInfo> getUnfilteredResolveList() {
        return mUnfilteredResolveList;
    }

    /**
     * @return true if all items in the display list are defined as browsers by
     *         ResolveInfo.handleAllWebDataURI
@@ -576,7 +579,7 @@ public class ResolverListAdapter extends BaseAdapter {

    Drawable loadIconForResolveInfo(ResolveInfo ri) {
        // Load icons based on the current process. If in work profile icons should be badged.
        return makePresentationGetter(ri).getIcon(Process.myUserHandle());
        return makePresentationGetter(ri).getIcon(mResolverListController.getUserHandle());
    }

    void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
Loading