226 lines
8.8 KiB
Java
226 lines
8.8 KiB
Java
/*
|
|
* 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 <PageViewT> the type of the widget that represents the contents of a page in this adapter
|
|
* @param <SinglePageAdapterT> the type of a "root" adapter class to be instantiated and included in
|
|
* the per-profile records.
|
|
* @param <ListAdapterT> 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<PageViewT, SinglePageAdapterT> {
|
|
/**
|
|
* 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<SinglePageAdapterT, ListAdapterT> mListAdapterExtractor;
|
|
private final AdapterBinder<PageViewT, SinglePageAdapterT> mAdapterBinder;
|
|
private final Supplier<ViewGroup> mPageViewInflater;
|
|
private final Supplier<Optional<Integer>> mContainerBottomPaddingOverrideSupplier;
|
|
|
|
private final ImmutableList<GenericProfileDescriptor<PageViewT, SinglePageAdapterT>> mItems;
|
|
|
|
GenericMultiProfilePagerAdapter(
|
|
Context context,
|
|
Function<SinglePageAdapterT, ListAdapterT> listAdapterExtractor,
|
|
AdapterBinder<PageViewT, SinglePageAdapterT> adapterBinder,
|
|
ImmutableList<SinglePageAdapterT> adapters,
|
|
EmptyStateProvider emptyStateProvider,
|
|
Supplier<Boolean> workProfileQuietModeChecker,
|
|
@Profile int defaultProfile,
|
|
UserHandle workProfileUserHandle,
|
|
Supplier<ViewGroup> pageViewInflater,
|
|
Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier) {
|
|
super(
|
|
context,
|
|
/* currentPage= */ defaultProfile,
|
|
emptyStateProvider,
|
|
workProfileQuietModeChecker,
|
|
workProfileUserHandle);
|
|
|
|
mListAdapterExtractor = listAdapterExtractor;
|
|
mAdapterBinder = adapterBinder;
|
|
mPageViewInflater = pageViewInflater;
|
|
mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier;
|
|
|
|
ImmutableList.Builder<GenericProfileDescriptor<PageViewT, SinglePageAdapterT>> items =
|
|
new ImmutableList.Builder<>();
|
|
for (SinglePageAdapterT adapter : adapters) {
|
|
items.add(createProfileDescriptor(adapter));
|
|
}
|
|
mItems = items.build();
|
|
}
|
|
|
|
private GenericProfileDescriptor<PageViewT, SinglePageAdapterT>
|
|
createProfileDescriptor(SinglePageAdapterT adapter) {
|
|
return new GenericProfileDescriptor<>(mPageViewInflater.get(), adapter);
|
|
}
|
|
|
|
@Override
|
|
protected GenericProfileDescriptor<PageViewT, SinglePageAdapterT> 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<Integer> 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<PageViewT, SinglePageAdapterT> 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);
|
|
}
|
|
}
|
|
}
|