• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.intentresolver;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.os.UserHandle;
22 import android.util.Log;
23 import android.view.View;
24 import android.view.ViewGroup;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 
28 import com.google.common.collect.ImmutableList;
29 
30 import java.util.Optional;
31 import java.util.function.Function;
32 import java.util.function.Supplier;
33 
34 /**
35  * Implementation of {@link AbstractMultiProfilePagerAdapter} that consolidates the variation in
36  * existing implementations; most overrides were only to vary type signatures (which are better
37  * represented via generic types), and a few minor behavioral customizations are now implemented
38  * through small injectable delegate classes.
39  * TODO: now that the existing implementations are shown to be expressible in terms of this new
40  * generic type, merge up into the base class and simplify the public APIs.
41  * TODO: attempt to further restrict visibility in the methods we expose.
42  * TODO: deprecate and audit/fix usages of any methods that refer to the "active" or "inactive"
43  * adapters; these were marked {@link VisibleForTesting} and their usage seems like an accident
44  * waiting to happen since clients seem to make assumptions about which adapter will be "active" in
45  * a particular context, and more explicit APIs would make sure those were valid.
46  * TODO: consider renaming legacy methods (e.g. why do we know it's a "list", not just a "page"?)
47  *
48  * @param <PageViewT> the type of the widget that represents the contents of a page in this adapter
49  * @param <SinglePageAdapterT> the type of a "root" adapter class to be instantiated and included in
50  * the per-profile records.
51  * @param <ListAdapterT> the concrete type of a {@link ResolverListAdapter} implementation to
52  * control the contents of a given per-profile list. This is provided for convenience, since it must
53  * be possible to get the list adapter from the page adapter via our {@link mListAdapterExtractor}.
54  *
55  * TODO: this class doesn't make any explicit usage of the {@link ResolverListAdapter} API, so the
56  * type constraint can probably be dropped once the API is merged upwards and cleaned.
57  */
58 class GenericMultiProfilePagerAdapter<
59         PageViewT extends ViewGroup,
60         SinglePageAdapterT,
61         ListAdapterT extends ResolverListAdapter> extends AbstractMultiProfilePagerAdapter {
62 
63     /** Delegate to set up a given adapter and page view to be used together. */
64     public interface AdapterBinder<PageViewT, SinglePageAdapterT> {
65         /**
66          * The given {@code view} will be associated with the given {@code adapter}. Do any work
67          * necessary to configure them compatibly, introduce them to each other, etc.
68          */
bind(PageViewT view, SinglePageAdapterT adapter)69         void bind(PageViewT view, SinglePageAdapterT adapter);
70     }
71 
72     private final Function<SinglePageAdapterT, ListAdapterT> mListAdapterExtractor;
73     private final AdapterBinder<PageViewT, SinglePageAdapterT> mAdapterBinder;
74     private final Supplier<ViewGroup> mPageViewInflater;
75     private final Supplier<Optional<Integer>> mContainerBottomPaddingOverrideSupplier;
76 
77     private final ImmutableList<GenericProfileDescriptor<PageViewT, SinglePageAdapterT>> mItems;
78 
GenericMultiProfilePagerAdapter( Context context, Function<SinglePageAdapterT, ListAdapterT> listAdapterExtractor, AdapterBinder<PageViewT, SinglePageAdapterT> adapterBinder, ImmutableList<SinglePageAdapterT> adapters, EmptyStateProvider emptyStateProvider, Supplier<Boolean> workProfileQuietModeChecker, @Profile int defaultProfile, UserHandle workProfileUserHandle, UserHandle cloneProfileUserHandle, Supplier<ViewGroup> pageViewInflater, Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier)79     GenericMultiProfilePagerAdapter(
80             Context context,
81             Function<SinglePageAdapterT, ListAdapterT> listAdapterExtractor,
82             AdapterBinder<PageViewT, SinglePageAdapterT> adapterBinder,
83             ImmutableList<SinglePageAdapterT> adapters,
84             EmptyStateProvider emptyStateProvider,
85             Supplier<Boolean> workProfileQuietModeChecker,
86             @Profile int defaultProfile,
87             UserHandle workProfileUserHandle,
88             UserHandle cloneProfileUserHandle,
89             Supplier<ViewGroup> pageViewInflater,
90             Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier) {
91         super(
92                 context,
93                 /* currentPage= */ defaultProfile,
94                 emptyStateProvider,
95                 workProfileQuietModeChecker,
96                 workProfileUserHandle,
97                 cloneProfileUserHandle);
98 
99         mListAdapterExtractor = listAdapterExtractor;
100         mAdapterBinder = adapterBinder;
101         mPageViewInflater = pageViewInflater;
102         mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier;
103 
104         ImmutableList.Builder<GenericProfileDescriptor<PageViewT, SinglePageAdapterT>> items =
105                 new ImmutableList.Builder<>();
106         for (SinglePageAdapterT adapter : adapters) {
107             items.add(createProfileDescriptor(adapter));
108         }
109         mItems = items.build();
110     }
111 
112     private GenericProfileDescriptor<PageViewT, SinglePageAdapterT>
createProfileDescriptor(SinglePageAdapterT adapter)113             createProfileDescriptor(SinglePageAdapterT adapter) {
114         return new GenericProfileDescriptor<>(mPageViewInflater.get(), adapter);
115     }
116 
117     @Override
getItem(int pageIndex)118     protected GenericProfileDescriptor<PageViewT, SinglePageAdapterT> getItem(int pageIndex) {
119         return mItems.get(pageIndex);
120     }
121 
122     @Override
getItemCount()123     public int getItemCount() {
124         return mItems.size();
125     }
126 
getListViewForIndex(int index)127     public PageViewT getListViewForIndex(int index) {
128         return getItem(index).mView;
129     }
130 
131     @Override
132     @VisibleForTesting
getAdapterForIndex(int index)133     public SinglePageAdapterT getAdapterForIndex(int index) {
134         return getItem(index).mAdapter;
135     }
136 
137     @Override
setupListAdapter(int pageIndex)138     protected void setupListAdapter(int pageIndex) {
139         mAdapterBinder.bind(getListViewForIndex(pageIndex), getAdapterForIndex(pageIndex));
140     }
141 
142     @Override
instantiateItem(ViewGroup container, int position)143     public ViewGroup instantiateItem(ViewGroup container, int position) {
144         setupListAdapter(position);
145         return super.instantiateItem(container, position);
146     }
147 
148     @Override
149     @Nullable
getListAdapterForUserHandle(UserHandle userHandle)150     protected ListAdapterT getListAdapterForUserHandle(UserHandle userHandle) {
151         if (getPersonalListAdapter().getUserHandle().equals(userHandle)
152                 || userHandle.equals(getCloneUserHandle())) {
153             return getPersonalListAdapter();
154         } else if (getWorkListAdapter() != null
155                 && getWorkListAdapter().getUserHandle().equals(userHandle)) {
156             return getWorkListAdapter();
157         }
158         return null;
159     }
160 
161     @Override
162     @VisibleForTesting
getActiveListAdapter()163     public ListAdapterT getActiveListAdapter() {
164         return mListAdapterExtractor.apply(getAdapterForIndex(getCurrentPage()));
165     }
166 
167     @Override
168     @VisibleForTesting
getInactiveListAdapter()169     public ListAdapterT getInactiveListAdapter() {
170         if (getCount() < 2) {
171             return null;
172         }
173         return mListAdapterExtractor.apply(getAdapterForIndex(1 - getCurrentPage()));
174     }
175 
176     @Override
getPersonalListAdapter()177     public ListAdapterT getPersonalListAdapter() {
178         return mListAdapterExtractor.apply(getAdapterForIndex(PROFILE_PERSONAL));
179     }
180 
181     @Override
getWorkListAdapter()182     public ListAdapterT getWorkListAdapter() {
183         if (!hasAdapterForIndex(PROFILE_WORK)) {
184             return null;
185         }
186         return mListAdapterExtractor.apply(getAdapterForIndex(PROFILE_WORK));
187     }
188 
189     @Override
getCurrentRootAdapter()190     protected SinglePageAdapterT getCurrentRootAdapter() {
191         return getAdapterForIndex(getCurrentPage());
192     }
193 
194     @Override
getActiveAdapterView()195     protected PageViewT getActiveAdapterView() {
196         return getListViewForIndex(getCurrentPage());
197     }
198 
199     @Override
getInactiveAdapterView()200     protected PageViewT getInactiveAdapterView() {
201         if (getCount() < 2) {
202             return null;
203         }
204         return getListViewForIndex(1 - getCurrentPage());
205     }
206 
207     @Override
setupContainerPadding(View container)208     protected void setupContainerPadding(View container) {
209         Optional<Integer> bottomPaddingOverride = mContainerBottomPaddingOverrideSupplier.get();
210         bottomPaddingOverride.ifPresent(paddingBottom ->
211                 container.setPadding(
212                     container.getPaddingLeft(),
213                     container.getPaddingTop(),
214                     container.getPaddingRight(),
215                     paddingBottom));
216     }
217 
hasAdapterForIndex(int pageIndex)218     private boolean hasAdapterForIndex(int pageIndex) {
219         return (pageIndex < getCount());
220     }
221 
222     // TODO: `ChooserActivity` also has a per-profile record type. Maybe the "multi-profile pager"
223     // should be the owner of all per-profile data (especially now that the API is generic)?
224     private static class GenericProfileDescriptor<PageViewT, SinglePageAdapterT> extends
225             ProfileDescriptor {
226         private final SinglePageAdapterT mAdapter;
227         private final PageViewT mView;
228 
GenericProfileDescriptor(ViewGroup rootView, SinglePageAdapterT adapter)229         GenericProfileDescriptor(ViewGroup rootView, SinglePageAdapterT adapter) {
230             super(rootView);
231             mAdapter = adapter;
232             mView = (PageViewT) rootView.findViewById(com.android.internal.R.id.resolver_list);
233         }
234     }
235 }
236