From 3f2db6f582dd9283ad8a582b7ed95c7ce9863399 Mon Sep 17 00:00:00 2001 From: Joshua Trask Date: Thu, 8 Dec 2022 17:50:57 -0500 Subject: Introduce GenericMultiProfilePagerAdapter This is an intermediate step in simplifying the full class hierarchy around the MultiProfilePagerAdapters in support of our general interest in clarifying/decoupling the chooser/resolver relationship, and especially in support of our specific goals in being able to build these UIs outside of the chooser/resolver activities (for the embedded sharesheet project). The new class takes over (almost) all of the responsibilities of the two legacy adapter implementations, leaving those components (almost) trivially implemented in terms of this new unified implementation. The remaining responsibilities are extracted to delegate interfaces with corresponding implementations in the legacy components that get injected in the initialization of the new "Generic" base class. (This is just one possible abstraction that I've chosen for now because it trivially preserves the behavior while setting up a structure that's easier to manipulate in our refactoring. In the future, we could consider alternatives -- maybe using simpler "setter" APIs, or adjusting our design to pull these responsibilities out of the adapter altogether.) Following this CL, we should merge this new component up into the base `AbstractMultiProfilePagerAdapter`, then close that API to further extension. We may also remove the use of inheritance as the model for pre-specifying "chooser" vs. "resolver" configurations; we'd only have a single (generic) class, with any behavioral variation configured at initialization time. Notably, the use of generics in the new class replaces a lot of duplication where the chooser implementation just needed to provide overrides to specify the narrower chooser-specific types. I expect that the legacy code has several other places where the chooser-specific version of a class exists largely for these "glue code" responsibilities of switching the entire design over to using the chooser "family" of concrete classes. There's lots of problems with that approach. It's unnecessarily flexible/complex (our application doesn't require runtime polymorphism to select between chooser and resolver implementations); it creates a lot of complex/error-prone situations where the safety of our downcasts relies on our knowledge of how the system is configured at runtime (and our trust in the participating components to honor their [undocumented] contracts); it creates unnecessary coupling and duplication as the configuration has to be pushed through to indirect dependents; and in practice we often have chooser extend resolver instead of pulling up a base class, so the chooser API surfaces grow to accommodate every customization we ever support for the resolver case (often without any particular documentation or contracts for subclass implementors). As in this CL, generics are often the simplest way to preserve the legacy APIs while reducing duplication, and they provide all the flexibility we need since our "app configurations" are static at compile-time. When more runtime flexibility is required, GoF "creational" and "behavioral" patterns specify well-defined alternatives that we should target strictly in our refactorings to prevent the kinds of ad-hoc scope creep that results in our legacy components' current incoherent mix of responsibilities. Test: atest IntentResolverUnitTests Bug: 202167050 Change-Id: I2cde7ee2e0cbed43499bfe457b6b99f239bdfb51 --- .../AbstractMultiProfilePagerAdapter.java | 8 +- .../android/intentresolver/ChooserActivity.java | 2 +- .../ChooserMultiProfilePagerAdapter.java | 225 +++++++++------------ .../GenericMultiProfilePagerAdapter.java | 225 +++++++++++++++++++++ .../ResolverMultiProfilePagerAdapter.java | 196 ++++++------------ 5 files changed, 391 insertions(+), 265 deletions(-) create mode 100644 java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java (limited to 'java/src') diff --git a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java index 8b0b10b0..17dbb8f2 100644 --- a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java @@ -16,8 +16,8 @@ package com.android.intentresolver; import android.annotation.IntDef; -import android.annotation.Nullable; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ContentResolver; @@ -173,6 +173,10 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { */ abstract ProfileDescriptor getItem(int pageIndex); + protected ViewGroup getEmptyStateView(int pageIndex) { + return getItem(pageIndex).getEmptyStateView(); + } + /** * Returns the number of {@link ProfileDescriptor} objects. *

For a normal consumer device with only one user returns 1. @@ -432,7 +436,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { && isQuietModeEnabled(mWorkProfileUserHandle)); } - protected class ProfileDescriptor { + protected static class ProfileDescriptor { final ViewGroup rootView; private final ViewGroup mEmptyStateView; ProfileDescriptor(ViewGroup rootView) { diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 4682ec50..295d5b70 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -1923,7 +1923,7 @@ public class ChooserActivity extends ResolverActivity implements private ViewGroup getActiveEmptyStateView() { int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage(); - return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView(); + return mChooserMultiProfilePagerAdapter.getEmptyStateView(currentPage); } @Override // ResolverListCommunicator diff --git a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java index 93daa299..39d1fab0 100644 --- a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java @@ -16,11 +16,9 @@ package com.android.intentresolver; -import android.annotation.Nullable; import android.content.Context; import android.os.UserHandle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.GridLayoutManager; @@ -30,16 +28,21 @@ import androidx.viewpager.widget.PagerAdapter; import com.android.intentresolver.grid.ChooserGridAdapter; import com.android.internal.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +import java.util.Optional; +import java.util.function.Supplier; + /** * A {@link PagerAdapter} which describes the work and personal profile share sheet screens. */ @VisibleForTesting -public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter { +public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAdapter< + RecyclerView, ChooserGridAdapter, ChooserListAdapter> { private static final int SINGLE_CELL_SPAN_SIZE = 1; - private final ChooserProfileDescriptor[] mItems; - private int mBottomOffset; - private int mMaxTargetsPerRow; + private final ChooserProfileAdapterBinder mAdapterBinder; + private final BottomPaddingOverrideSupplier mBottomPaddingOverrideSupplier; ChooserMultiProfilePagerAdapter( Context context, @@ -48,12 +51,15 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd QuietModeManager quietModeManager, UserHandle workProfileUserHandle, int maxTargetsPerRow) { - super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager, - workProfileUserHandle); - mItems = new ChooserProfileDescriptor[] { - createProfileDescriptor(adapter) - }; - mMaxTargetsPerRow = maxTargetsPerRow; + this( + context, + new ChooserProfileAdapterBinder(maxTargetsPerRow), + ImmutableList.of(adapter), + emptyStateProvider, + quietModeManager, + /* defaultProfile= */ 0, + workProfileUserHandle, + new BottomPaddingOverrideSupplier(context)); } ChooserMultiProfilePagerAdapter( @@ -65,143 +71,104 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd @Profile int defaultProfile, UserHandle workProfileUserHandle, int maxTargetsPerRow) { - super(context, /* currentPage */ defaultProfile, emptyStateProvider, - quietModeManager, workProfileUserHandle); - mItems = new ChooserProfileDescriptor[] { - createProfileDescriptor(personalAdapter), - createProfileDescriptor(workAdapter) - }; - mMaxTargetsPerRow = maxTargetsPerRow; - } - - private ChooserProfileDescriptor createProfileDescriptor(ChooserGridAdapter adapter) { - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final ViewGroup rootView = - (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false); - ChooserProfileDescriptor profileDescriptor = - new ChooserProfileDescriptor(rootView, adapter); - profileDescriptor.recyclerView.setAccessibilityDelegateCompat( - new ChooserRecyclerViewAccessibilityDelegate(profileDescriptor.recyclerView)); - return profileDescriptor; + this( + context, + new ChooserProfileAdapterBinder(maxTargetsPerRow), + ImmutableList.of(personalAdapter, workAdapter), + emptyStateProvider, + quietModeManager, + defaultProfile, + workProfileUserHandle, + new BottomPaddingOverrideSupplier(context)); + } + + private ChooserMultiProfilePagerAdapter( + Context context, + ChooserProfileAdapterBinder adapterBinder, + ImmutableList gridAdapters, + EmptyStateProvider emptyStateProvider, + QuietModeManager quietModeManager, + @Profile int defaultProfile, + UserHandle workProfileUserHandle, + BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier) { + super( + context, + gridAdapter -> gridAdapter.getListAdapter(), + adapterBinder, + gridAdapters, + emptyStateProvider, + quietModeManager, + defaultProfile, + workProfileUserHandle, + () -> makeProfileView(context), + bottomPaddingOverrideSupplier); + mAdapterBinder = adapterBinder; + mBottomPaddingOverrideSupplier = bottomPaddingOverrideSupplier; } public void setMaxTargetsPerRow(int maxTargetsPerRow) { - mMaxTargetsPerRow = maxTargetsPerRow; - } - - RecyclerView getListViewForIndex(int index) { - return getItem(index).recyclerView; + mAdapterBinder.setMaxTargetsPerRow(maxTargetsPerRow); } - @Override - ChooserProfileDescriptor getItem(int pageIndex) { - return mItems[pageIndex]; + public void setEmptyStateBottomOffset(int bottomOffset) { + mBottomPaddingOverrideSupplier.setEmptyStateBottomOffset(bottomOffset); } - @Override - int getItemCount() { - return mItems.length; + private static ViewGroup makeProfileView(Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + ViewGroup rootView = (ViewGroup) inflater.inflate( + R.layout.chooser_list_per_profile, null, false); + RecyclerView recyclerView = rootView.findViewById(com.android.internal.R.id.resolver_list); + recyclerView.setAccessibilityDelegateCompat( + new ChooserRecyclerViewAccessibilityDelegate(recyclerView)); + return rootView; } - @Override - @VisibleForTesting - public ChooserGridAdapter getAdapterForIndex(int pageIndex) { - return mItems[pageIndex].chooserGridAdapter; - } + private static class BottomPaddingOverrideSupplier implements Supplier> { + private final Context mContext; + private int mBottomOffset; - @Override - @Nullable - ChooserListAdapter getListAdapterForUserHandle(UserHandle userHandle) { - if (getActiveListAdapter().getUserHandle().equals(userHandle)) { - return getActiveListAdapter(); - } else if (getInactiveListAdapter() != null - && getInactiveListAdapter().getUserHandle().equals(userHandle)) { - return getInactiveListAdapter(); + BottomPaddingOverrideSupplier(Context context) { + mContext = context; } - return null; - } - - @Override - void setupListAdapter(int pageIndex) { - final RecyclerView recyclerView = getItem(pageIndex).recyclerView; - ChooserGridAdapter chooserGridAdapter = getItem(pageIndex).chooserGridAdapter; - GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); - glm.setSpanCount(mMaxTargetsPerRow); - 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 getActiveListAdapter() { - return getAdapterForIndex(getCurrentPage()).getListAdapter(); - } - - @Override - @VisibleForTesting - public ChooserListAdapter getInactiveListAdapter() { - if (getCount() == 1) { - return null; + public void setEmptyStateBottomOffset(int bottomOffset) { + mBottomOffset = bottomOffset; } - return getAdapterForIndex(1 - getCurrentPage()).getListAdapter(); - } - - @Override - public ResolverListAdapter getPersonalListAdapter() { - return getAdapterForIndex(PROFILE_PERSONAL).getListAdapter(); - } - - @Override - @Nullable - public ResolverListAdapter getWorkListAdapter() { - return getAdapterForIndex(PROFILE_WORK).getListAdapter(); - } - @Override - ChooserGridAdapter getCurrentRootAdapter() { - return getAdapterForIndex(getCurrentPage()); + public Optional get() { + int initialBottomPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.resolver_empty_state_container_padding_bottom); + return Optional.of(initialBottomPadding + mBottomOffset); + } } - @Override - RecyclerView getActiveAdapterView() { - return getListViewForIndex(getCurrentPage()); - } + private static class ChooserProfileAdapterBinder implements + AdapterBinder { + private int mMaxTargetsPerRow; - @Override - @Nullable - RecyclerView getInactiveAdapterView() { - if (getCount() == 1) { - return null; + ChooserProfileAdapterBinder(int maxTargetsPerRow) { + mMaxTargetsPerRow = maxTargetsPerRow; } - return getListViewForIndex(1 - getCurrentPage()); - } - void setEmptyStateBottomOffset(int bottomOffset) { - mBottomOffset = bottomOffset; - } - - @Override - protected void setupContainerPadding(View container) { - int initialBottomPadding = getContext().getResources().getDimensionPixelSize( - R.dimen.resolver_empty_state_container_padding_bottom); - container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), - container.getPaddingRight(), initialBottomPadding + mBottomOffset); - } + public void setMaxTargetsPerRow(int maxTargetsPerRow) { + mMaxTargetsPerRow = maxTargetsPerRow; + } - class ChooserProfileDescriptor extends ProfileDescriptor { - private ChooserGridAdapter chooserGridAdapter; - private RecyclerView recyclerView; - ChooserProfileDescriptor(ViewGroup rootView, ChooserGridAdapter adapter) { - super(rootView); - chooserGridAdapter = adapter; - recyclerView = rootView.findViewById(com.android.internal.R.id.resolver_list); + @Override + public void bind( + RecyclerView recyclerView, ChooserGridAdapter chooserGridAdapter) { + GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); + glm.setSpanCount(mMaxTargetsPerRow); + glm.setSpanSizeLookup( + new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + return chooserGridAdapter.shouldCellSpan(position) + ? SINGLE_CELL_SPAN_SIZE + : glm.getSpanCount(); + } + }); } } } diff --git a/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java new file mode 100644 index 00000000..9bbdf7c7 --- /dev/null +++ b/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2022 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.intentresolver; + +import android.annotation.Nullable; +import android.content.Context; +import android.os.UserHandle; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; + +import com.google.common.collect.ImmutableList; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Implementation of {@link AbstractMultiProfilePagerAdapter} that consolidates the variation in + * existing implementations; most overrides were only to vary type signatures (which are better + * represented via generic types), and a few minor behavioral customizations are now implemented + * through small injectable delegate classes. + * TODO: now that the existing implementations are shown to be expressible in terms of this new + * generic type, merge up into the base class and simplify the public APIs. + * TODO: attempt to further restrict visibility in the methods we expose. + * TODO: deprecate and audit/fix usages of any methods that refer to the "active" or "inactive" + * adapters; these were marked {@link VisibleForTesting} and their usage seems like an accident + * waiting to happen since clients seem to make assumptions about which adapter will be "active" in + * a particular context, and more explicit APIs would make sure those were valid. + * TODO: consider renaming legacy methods (e.g. why do we know it's a "list", not just a "page"?) + * + * @param the type of the widget that represents the contents of a page in this adapter + * @param the type of a "root" adapter class to be instantiated and included in + * the per-profile records. + * @param the concrete type of a {@link ResolverListAdapter} implementation to + * control the contents of a given per-profile list. This is provided for convenience, since it must + * be possible to get the list adapter from the page adapter via our {@link mListAdapterExtractor}. + * + * TODO: this class doesn't make any explicit usage of the {@link ResolverListAdapter} API, so the + * type constraint can probably be dropped once the API is merged upwards and cleaned. + */ +class GenericMultiProfilePagerAdapter< + PageViewT extends ViewGroup, + SinglePageAdapterT, + ListAdapterT extends ResolverListAdapter> extends AbstractMultiProfilePagerAdapter { + + /** Delegate to set up a given adapter and page view to be used together. */ + public interface AdapterBinder { + /** + * The given {@code view} will be associated with the given {@code adapter}. Do any work + * necessary to configure them compatibly, introduce them to each other, etc. + */ + void bind(PageViewT view, SinglePageAdapterT adapter); + } + + private final Function mListAdapterExtractor; + private final AdapterBinder mAdapterBinder; + private final Supplier mPageViewInflater; + private final Supplier> mContainerBottomPaddingOverrideSupplier; + + private final ImmutableList> mItems; + + GenericMultiProfilePagerAdapter( + Context context, + Function listAdapterExtractor, + AdapterBinder adapterBinder, + ImmutableList adapters, + EmptyStateProvider emptyStateProvider, + QuietModeManager quietModeManager, + @Profile int defaultProfile, + UserHandle workProfileUserHandle, + Supplier pageViewInflater, + Supplier> containerBottomPaddingOverrideSupplier) { + super( + context, + /* currentPage= */ defaultProfile, + emptyStateProvider, + quietModeManager, + workProfileUserHandle); + + mListAdapterExtractor = listAdapterExtractor; + mAdapterBinder = adapterBinder; + mPageViewInflater = pageViewInflater; + mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier; + + ImmutableList.Builder> items = + new ImmutableList.Builder<>(); + for (SinglePageAdapterT adapter : adapters) { + items.add(createProfileDescriptor(adapter)); + } + mItems = items.build(); + } + + private GenericProfileDescriptor + createProfileDescriptor(SinglePageAdapterT adapter) { + return new GenericProfileDescriptor<>(mPageViewInflater.get(), adapter); + } + + @Override + protected GenericProfileDescriptor getItem(int pageIndex) { + return mItems.get(pageIndex); + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + public PageViewT getListViewForIndex(int index) { + return getItem(index).mView; + } + + @Override + @VisibleForTesting + public SinglePageAdapterT getAdapterForIndex(int index) { + return getItem(index).mAdapter; + } + + @Override + protected void setupListAdapter(int pageIndex) { + mAdapterBinder.bind(getListViewForIndex(pageIndex), getAdapterForIndex(pageIndex)); + } + + @Override + public ViewGroup instantiateItem(ViewGroup container, int position) { + setupListAdapter(position); + return super.instantiateItem(container, position); + } + + @Override + @Nullable + protected ListAdapterT getListAdapterForUserHandle(UserHandle userHandle) { + if (getActiveListAdapter().getUserHandle().equals(userHandle)) { + return getActiveListAdapter(); + } + if ((getInactiveListAdapter() != null) && getInactiveListAdapter().getUserHandle().equals( + userHandle)) { + return getInactiveListAdapter(); + } + return null; + } + + @Override + @VisibleForTesting + public ListAdapterT getActiveListAdapter() { + return mListAdapterExtractor.apply(getAdapterForIndex(getCurrentPage())); + } + + @Override + @VisibleForTesting + public ListAdapterT getInactiveListAdapter() { + if (getCount() < 2) { + return null; + } + return mListAdapterExtractor.apply(getAdapterForIndex(1 - getCurrentPage())); + } + + @Override + public ListAdapterT getPersonalListAdapter() { + return mListAdapterExtractor.apply(getAdapterForIndex(PROFILE_PERSONAL)); + } + + @Override + public ListAdapterT getWorkListAdapter() { + return mListAdapterExtractor.apply(getAdapterForIndex(PROFILE_WORK)); + } + + @Override + protected SinglePageAdapterT getCurrentRootAdapter() { + return getAdapterForIndex(getCurrentPage()); + } + + @Override + protected PageViewT getActiveAdapterView() { + return getListViewForIndex(getCurrentPage()); + } + + @Override + protected PageViewT getInactiveAdapterView() { + if (getCount() < 2) { + return null; + } + return getListViewForIndex(1 - getCurrentPage()); + } + + @Override + protected void setupContainerPadding(View container) { + Optional bottomPaddingOverride = mContainerBottomPaddingOverrideSupplier.get(); + bottomPaddingOverride.ifPresent(paddingBottom -> + container.setPadding( + container.getPaddingLeft(), + container.getPaddingTop(), + container.getPaddingRight(), + paddingBottom)); + } + + // TODO: `ChooserActivity` also has a per-profile record type. Maybe the "multi-profile pager" + // should be the owner of all per-profile data (especially now that the API is generic)? + private static class GenericProfileDescriptor extends + ProfileDescriptor { + private final SinglePageAdapterT mAdapter; + private final PageViewT mView; + + GenericProfileDescriptor(ViewGroup rootView, SinglePageAdapterT adapter) { + super(rootView); + mAdapter = adapter; + mView = (PageViewT) rootView.findViewById(com.android.internal.R.id.resolver_list); + } + } +} diff --git a/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java index 8cf65529..65de9409 100644 --- a/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java @@ -16,11 +16,9 @@ package com.android.intentresolver; -import android.annotation.Nullable; import android.content.Context; import android.os.UserHandle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import android.widget.ListView; @@ -28,25 +26,33 @@ import androidx.viewpager.widget.PagerAdapter; import com.android.internal.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +import java.util.Optional; +import java.util.function.Supplier; + /** * A {@link PagerAdapter} which describes the work and personal profile intent resolver screens. */ @VisibleForTesting -public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter { - - private final ResolverProfileDescriptor[] mItems; - private boolean mUseLayoutWithDefault; +public class ResolverMultiProfilePagerAdapter extends + GenericMultiProfilePagerAdapter { + private final BottomPaddingOverrideSupplier mBottomPaddingOverrideSupplier; - ResolverMultiProfilePagerAdapter(Context context, + ResolverMultiProfilePagerAdapter( + Context context, ResolverListAdapter adapter, EmptyStateProvider emptyStateProvider, QuietModeManager quietModeManager, UserHandle workProfileUserHandle) { - super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager, - workProfileUserHandle); - mItems = new ResolverProfileDescriptor[] { - createProfileDescriptor(adapter) - }; + this( + context, + ImmutableList.of(adapter), + emptyStateProvider, + quietModeManager, + /* defaultProfile= */ 0, + workProfileUserHandle, + new BottomPaddingOverrideSupplier()); } ResolverMultiProfilePagerAdapter(Context context, @@ -56,129 +62,53 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA QuietModeManager quietModeManager, @Profile int defaultProfile, UserHandle workProfileUserHandle) { - super(context, /* currentPage */ defaultProfile, emptyStateProvider, quietModeManager, - workProfileUserHandle); - mItems = new ResolverProfileDescriptor[] { - createProfileDescriptor(personalAdapter), - createProfileDescriptor(workAdapter) - }; - } - - private ResolverProfileDescriptor createProfileDescriptor( - ResolverListAdapter adapter) { - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final ViewGroup rootView = - (ViewGroup) inflater.inflate(R.layout.resolver_list_per_profile, null, false); - return new ResolverProfileDescriptor(rootView, adapter); - } - - ListView getListViewForIndex(int index) { - return getItem(index).listView; - } - - @Override - ResolverProfileDescriptor getItem(int pageIndex) { - return mItems[pageIndex]; - } - - @Override - int getItemCount() { - return mItems.length; - } - - @Override - void setupListAdapter(int pageIndex) { - final ListView listView = getItem(pageIndex).listView; - listView.setAdapter(getItem(pageIndex).resolverListAdapter); - } - - @Override - @VisibleForTesting - public ResolverListAdapter getAdapterForIndex(int pageIndex) { - return mItems[pageIndex].resolverListAdapter; - } - - @Override - public ViewGroup instantiateItem(ViewGroup container, int position) { - setupListAdapter(position); - return super.instantiateItem(container, position); - } - - @Override - @Nullable - ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle) { - if (getActiveListAdapter().getUserHandle().equals(userHandle)) { - return getActiveListAdapter(); - } else if (getInactiveListAdapter() != null - && getInactiveListAdapter().getUserHandle().equals(userHandle)) { - return getInactiveListAdapter(); - } - return null; - } - - @Override - @VisibleForTesting - public ResolverListAdapter getActiveListAdapter() { - return getAdapterForIndex(getCurrentPage()); - } - - @Override - @VisibleForTesting - public ResolverListAdapter getInactiveListAdapter() { - if (getCount() == 1) { - return null; - } - return getAdapterForIndex(1 - getCurrentPage()); - } - - @Override - public ResolverListAdapter getPersonalListAdapter() { - return getAdapterForIndex(PROFILE_PERSONAL); - } - - @Override - @Nullable - public ResolverListAdapter getWorkListAdapter() { - return getAdapterForIndex(PROFILE_WORK); - } - - @Override - ResolverListAdapter getCurrentRootAdapter() { - return getActiveListAdapter(); - } - - @Override - ListView getActiveAdapterView() { - return getListViewForIndex(getCurrentPage()); - } - - @Override - @Nullable - ViewGroup getInactiveAdapterView() { - if (getCount() == 1) { - return null; + this( + context, + ImmutableList.of(personalAdapter, workAdapter), + emptyStateProvider, + quietModeManager, + defaultProfile, + workProfileUserHandle, + new BottomPaddingOverrideSupplier()); + } + + private ResolverMultiProfilePagerAdapter( + Context context, + ImmutableList listAdapters, + EmptyStateProvider emptyStateProvider, + QuietModeManager quietModeManager, + @Profile int defaultProfile, + UserHandle workProfileUserHandle, + BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier) { + super( + context, + listAdapter -> listAdapter, + (listView, bindAdapter) -> listView.setAdapter(bindAdapter), + listAdapters, + emptyStateProvider, + quietModeManager, + defaultProfile, + workProfileUserHandle, + () -> (ViewGroup) LayoutInflater.from(context).inflate( + R.layout.resolver_list_per_profile, null, false), + bottomPaddingOverrideSupplier); + mBottomPaddingOverrideSupplier = bottomPaddingOverrideSupplier; + } + + public void setUseLayoutWithDefault(boolean useLayoutWithDefault) { + mBottomPaddingOverrideSupplier.setUseLayoutWithDefault(useLayoutWithDefault); + } + + private static class BottomPaddingOverrideSupplier implements Supplier> { + private boolean mUseLayoutWithDefault; + + public void setUseLayoutWithDefault(boolean useLayoutWithDefault) { + mUseLayoutWithDefault = useLayoutWithDefault; } - return getListViewForIndex(1 - getCurrentPage()); - } - - void setUseLayoutWithDefault(boolean useLayoutWithDefault) { - mUseLayoutWithDefault = useLayoutWithDefault; - } - - @Override - protected void setupContainerPadding(View container) { - int bottom = mUseLayoutWithDefault ? container.getPaddingBottom() : 0; - container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), - container.getPaddingRight(), bottom); - } - class ResolverProfileDescriptor extends ProfileDescriptor { - private ResolverListAdapter resolverListAdapter; - final ListView listView; - ResolverProfileDescriptor(ViewGroup rootView, ResolverListAdapter adapter) { - super(rootView); - resolverListAdapter = adapter; - listView = rootView.findViewById(com.android.internal.R.id.resolver_list); + @Override + public Optional get() { + return mUseLayoutWithDefault ? Optional.empty() : Optional.of(0); } } } -- cgit v1.2.3-59-g8ed1b