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