1 /*
2  * Copyright (C) 2024 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.profiles;
18 
19 import android.content.Context;
20 import android.os.UserHandle;
21 import android.view.LayoutInflater;
22 import android.view.ViewGroup;
23 
24 import androidx.recyclerview.widget.GridLayoutManager;
25 import androidx.recyclerview.widget.RecyclerView;
26 import androidx.viewpager.widget.PagerAdapter;
27 
28 import com.android.intentresolver.ChooserListAdapter;
29 import com.android.intentresolver.ChooserRecyclerViewAccessibilityDelegate;
30 import com.android.intentresolver.R;
31 import com.android.intentresolver.emptystate.EmptyStateProvider;
32 import com.android.intentresolver.grid.ChooserGridAdapter;
33 import com.android.intentresolver.measurements.Tracer;
34 
35 import com.google.common.collect.ImmutableList;
36 
37 import java.util.Optional;
38 import java.util.function.Supplier;
39 
40 /**
41  * A {@link PagerAdapter} which describes the work and personal profile share sheet screens.
42  */
43 public class ChooserMultiProfilePagerAdapter extends MultiProfilePagerAdapter<
44         RecyclerView, ChooserGridAdapter, ChooserListAdapter> {
45     private static final int SINGLE_CELL_SPAN_SIZE = 1;
46 
47     private final ChooserProfileAdapterBinder mAdapterBinder;
48     private final BottomPaddingOverrideSupplier mBottomPaddingOverrideSupplier;
49 
ChooserMultiProfilePagerAdapter( Context context, ImmutableList<TabConfig<ChooserGridAdapter>> tabs, EmptyStateProvider emptyStateProvider, Supplier<Boolean> workProfileQuietModeChecker, @ProfileType int defaultProfile, UserHandle workProfileUserHandle, UserHandle cloneProfileUserHandle, int maxTargetsPerRow)50     public ChooserMultiProfilePagerAdapter(
51             Context context,
52             ImmutableList<TabConfig<ChooserGridAdapter>> tabs,
53             EmptyStateProvider emptyStateProvider,
54             Supplier<Boolean> workProfileQuietModeChecker,
55             @ProfileType int defaultProfile,
56             UserHandle workProfileUserHandle,
57             UserHandle cloneProfileUserHandle,
58             int maxTargetsPerRow) {
59         this(
60                 context,
61                 new ChooserProfileAdapterBinder(maxTargetsPerRow),
62                 tabs,
63                 emptyStateProvider,
64                 workProfileQuietModeChecker,
65                 defaultProfile,
66                 workProfileUserHandle,
67                 cloneProfileUserHandle,
68                 new BottomPaddingOverrideSupplier(context));
69     }
70 
ChooserMultiProfilePagerAdapter( Context context, ChooserProfileAdapterBinder adapterBinder, ImmutableList<TabConfig<ChooserGridAdapter>> tabs, EmptyStateProvider emptyStateProvider, Supplier<Boolean> workProfileQuietModeChecker, @ProfileType int defaultProfile, UserHandle workProfileUserHandle, UserHandle cloneProfileUserHandle, BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier)71     private ChooserMultiProfilePagerAdapter(
72             Context context,
73             ChooserProfileAdapterBinder adapterBinder,
74             ImmutableList<TabConfig<ChooserGridAdapter>> tabs,
75             EmptyStateProvider emptyStateProvider,
76             Supplier<Boolean> workProfileQuietModeChecker,
77             @ProfileType int defaultProfile,
78             UserHandle workProfileUserHandle,
79             UserHandle cloneProfileUserHandle,
80             BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier) {
81         super(
82                 gridAdapter -> gridAdapter.getListAdapter(),
83                 adapterBinder,
84                 tabs,
85                 emptyStateProvider,
86                 workProfileQuietModeChecker,
87                 defaultProfile,
88                 workProfileUserHandle,
89                 cloneProfileUserHandle,
90                 () -> makeProfileView(context),
91                 bottomPaddingOverrideSupplier);
92         mAdapterBinder = adapterBinder;
93         mBottomPaddingOverrideSupplier = bottomPaddingOverrideSupplier;
94     }
95 
setMaxTargetsPerRow(int maxTargetsPerRow)96     public void setMaxTargetsPerRow(int maxTargetsPerRow) {
97         mAdapterBinder.setMaxTargetsPerRow(maxTargetsPerRow);
98     }
99 
setEmptyStateBottomOffset(int bottomOffset)100     public void setEmptyStateBottomOffset(int bottomOffset) {
101         mBottomPaddingOverrideSupplier.setEmptyStateBottomOffset(bottomOffset);
102         setupContainerPadding();
103     }
104 
105     /**
106      * Notify adapter about the drawer's collapse state. This will affect the app divider's
107      * visibility.
108      */
setIsCollapsed(boolean isCollapsed)109     public void setIsCollapsed(boolean isCollapsed) {
110         for (int i = 0, size = getItemCount(); i < size; i++) {
111             getPageAdapterForIndex(i).setAzLabelVisibility(!isCollapsed);
112         }
113     }
114 
makeProfileView(Context context)115     private static ViewGroup makeProfileView(Context context) {
116         LayoutInflater inflater = LayoutInflater.from(context);
117         ViewGroup rootView =
118                 (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile_wrap, null, false);
119         RecyclerView recyclerView = rootView.findViewById(com.android.internal.R.id.resolver_list);
120         recyclerView.setAccessibilityDelegateCompat(
121                 new ChooserRecyclerViewAccessibilityDelegate(recyclerView));
122         return rootView;
123     }
124 
125     @Override
onHandlePackagesChanged( ChooserListAdapter listAdapter, boolean waitingToEnableWorkProfile)126     public boolean onHandlePackagesChanged(
127             ChooserListAdapter listAdapter, boolean waitingToEnableWorkProfile) {
128         // TODO: why do we need to do the extra `notifyDataSetChanged()` in (only) the Chooser case?
129         getActiveListAdapter().notifyDataSetChanged();
130         return super.onHandlePackagesChanged(listAdapter, waitingToEnableWorkProfile);
131     }
132 
133     @Override
rebuildTab(ChooserListAdapter listAdapter, boolean doPostProcessing)134     protected final boolean rebuildTab(ChooserListAdapter listAdapter, boolean doPostProcessing) {
135         if (doPostProcessing) {
136             Tracer.INSTANCE.beginAppTargetLoadingSection(listAdapter.getUserHandle());
137         }
138         return super.rebuildTab(listAdapter, doPostProcessing);
139     }
140 
141     /** Apply the specified {@code height} as the footer in each tab's adapter. */
setFooterHeightInEveryAdapter(int height)142     public void setFooterHeightInEveryAdapter(int height) {
143         for (int i = 0; i < getItemCount(); ++i) {
144             getPageAdapterForIndex(i).setFooterHeight(height);
145         }
146     }
147 
148     /** Cleanup system resources */
destroy()149     public void destroy() {
150         for (int i = 0, count = getItemCount(); i < count; i++) {
151             ChooserGridAdapter adapter = getPageAdapterForIndex(i);
152             if (adapter != null) {
153                 adapter.getListAdapter().onDestroy();
154             }
155         }
156     }
157 
158     private static class BottomPaddingOverrideSupplier implements Supplier<Optional<Integer>> {
159         private final Context mContext;
160         private int mBottomOffset;
161 
BottomPaddingOverrideSupplier(Context context)162         BottomPaddingOverrideSupplier(Context context) {
163             mContext = context;
164         }
165 
setEmptyStateBottomOffset(int bottomOffset)166         public void setEmptyStateBottomOffset(int bottomOffset) {
167             mBottomOffset = bottomOffset;
168         }
169 
170         @Override
get()171         public Optional<Integer> get() {
172             int initialBottomPadding = mContext.getResources().getDimensionPixelSize(
173                     R.dimen.resolver_empty_state_container_padding_bottom);
174             return Optional.of(initialBottomPadding + mBottomOffset);
175         }
176     }
177 
178     private static class ChooserProfileAdapterBinder implements
179             AdapterBinder<RecyclerView, ChooserGridAdapter> {
180         private int mMaxTargetsPerRow;
181 
ChooserProfileAdapterBinder(int maxTargetsPerRow)182         ChooserProfileAdapterBinder(int maxTargetsPerRow) {
183             mMaxTargetsPerRow = maxTargetsPerRow;
184         }
185 
setMaxTargetsPerRow(int maxTargetsPerRow)186         public void setMaxTargetsPerRow(int maxTargetsPerRow) {
187             mMaxTargetsPerRow = maxTargetsPerRow;
188         }
189 
190         @Override
bind( RecyclerView recyclerView, ChooserGridAdapter chooserGridAdapter)191         public void bind(
192                 RecyclerView recyclerView, ChooserGridAdapter chooserGridAdapter) {
193             GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
194             glm.setSpanCount(mMaxTargetsPerRow);
195             glm.setSpanSizeLookup(
196                     new GridLayoutManager.SpanSizeLookup() {
197                         @Override
198                         public int getSpanSize(int position) {
199                             return chooserGridAdapter.shouldCellSpan(position)
200                                     ? SINGLE_CELL_SPAN_SIZE
201                                     : glm.getSpanCount();
202                         }
203                     });
204         }
205     }
206 }
207