1 /* 2 * Copyright (C) 2018 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.settings.panel; 18 19 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_GROUP_SLICE_URI; 20 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI; 21 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI; 22 23 import android.app.settings.SettingsEnums; 24 import android.content.Context; 25 import android.net.Uri; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.LinearLayout; 30 31 import androidx.annotation.NonNull; 32 import androidx.annotation.VisibleForTesting; 33 import androidx.lifecycle.LiveData; 34 import androidx.recyclerview.widget.RecyclerView; 35 import androidx.slice.Slice; 36 import androidx.slice.widget.SliceView; 37 38 import com.android.settings.R; 39 import com.android.settings.overlay.FeatureFactory; 40 41 import com.google.android.setupdesign.DividerItemDecoration; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 import java.util.Map; 46 47 /** 48 * RecyclerView adapter for Slices in Settings Panels. 49 */ 50 public class PanelSlicesAdapter 51 extends RecyclerView.Adapter<PanelSlicesAdapter.SliceRowViewHolder> { 52 53 /** 54 * Maximum number of slices allowed on the panel view. 55 */ 56 @VisibleForTesting 57 static final int MAX_NUM_OF_SLICES = 6; 58 59 private final List<LiveData<Slice>> mSliceLiveData; 60 private final int mMetricsCategory; 61 private final PanelFragment mPanelFragment; 62 PanelSlicesAdapter( PanelFragment fragment, Map<Uri, LiveData<Slice>> sliceLiveData, int metricsCategory)63 public PanelSlicesAdapter( 64 PanelFragment fragment, Map<Uri, LiveData<Slice>> sliceLiveData, int metricsCategory) { 65 mPanelFragment = fragment; 66 mSliceLiveData = new ArrayList<>(sliceLiveData.values()); 67 mMetricsCategory = metricsCategory; 68 } 69 70 @NonNull 71 @Override onCreateViewHolder(@onNull ViewGroup viewGroup, int viewType)72 public SliceRowViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { 73 final Context context = viewGroup.getContext(); 74 final LayoutInflater inflater = LayoutInflater.from(context); 75 View view; 76 if (viewType == PanelContent.VIEW_TYPE_SLIDER) { 77 view = inflater.inflate(R.layout.panel_slice_slider_row, viewGroup, false); 78 } else if (viewType == PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON) { 79 view = inflater.inflate(R.layout.panel_slice_slider_row_large_icon, viewGroup, false); 80 } else { 81 view = inflater.inflate(R.layout.panel_slice_row, viewGroup, false); 82 } 83 84 return new SliceRowViewHolder(view); 85 } 86 87 @Override onBindViewHolder(@onNull SliceRowViewHolder sliceRowViewHolder, int position)88 public void onBindViewHolder(@NonNull SliceRowViewHolder sliceRowViewHolder, int position) { 89 sliceRowViewHolder.onBind(mSliceLiveData.get(position), position); 90 } 91 92 /** 93 * Return the number of available items in the adapter with max number of slices enforced. 94 */ 95 @Override getItemCount()96 public int getItemCount() { 97 return Math.min(mSliceLiveData.size(), MAX_NUM_OF_SLICES); 98 } 99 100 @Override getItemViewType(int position)101 public int getItemViewType(int position) { 102 return mPanelFragment.getPanelViewType(); 103 } 104 105 /** 106 * Return the available data from the adapter. If the number of Slices over the max number 107 * allowed, the list will only have the first MAX_NUM_OF_SLICES of slices. 108 */ 109 @VisibleForTesting getData()110 List<LiveData<Slice>> getData() { 111 return mSliceLiveData.subList(0, getItemCount()); 112 } 113 114 /** 115 * ViewHolder for binding Slices to SliceViews. 116 */ 117 public class SliceRowViewHolder extends RecyclerView.ViewHolder 118 implements DividerItemDecoration.DividedViewHolder { 119 120 private boolean mDividerAllowedAbove = true; 121 122 @VisibleForTesting 123 final SliceView sliceView; 124 @VisibleForTesting 125 final LinearLayout mSliceSliderLayout; 126 SliceRowViewHolder(View view)127 public SliceRowViewHolder(View view) { 128 super(view); 129 sliceView = view.findViewById(R.id.slice_view); 130 sliceView.setMode(SliceView.MODE_LARGE); 131 sliceView.setShowTitleItems(true); 132 mSliceSliderLayout = view.findViewById(R.id.slice_slider_layout); 133 } 134 135 /** 136 * Called when the view is displayed. 137 */ onBind(LiveData<Slice> sliceLiveData, int position)138 public void onBind(LiveData<Slice> sliceLiveData, int position) { 139 sliceLiveData.observe(mPanelFragment.getViewLifecycleOwner(), sliceView); 140 141 // Do not show the divider above media devices switcher slice per request 142 final Slice slice = sliceLiveData.getValue(); 143 if (slice == null || slice.getUri().equals(MEDIA_OUTPUT_INDICATOR_SLICE_URI)) { 144 mDividerAllowedAbove = false; 145 } else if (position == 0 && (slice.getUri().equals(MEDIA_OUTPUT_SLICE_URI) 146 || slice.getUri().equals(MEDIA_OUTPUT_GROUP_SLICE_URI))) { 147 sliceView.setClickable(false); 148 // Customize output switcher slice padding 149 final int padding = mPanelFragment.getResources().getDimensionPixelSize( 150 R.dimen.output_switcher_slice_padding_top); 151 mSliceSliderLayout.setPadding(mSliceSliderLayout.getPaddingLeft(), padding, 152 mSliceSliderLayout.getPaddingRight(), 153 padding); 154 } 155 156 // Log Panel interaction 157 sliceView.setOnSliceActionListener( 158 ((eventInfo, sliceItem) -> { 159 FeatureFactory.getFactory(sliceView.getContext()) 160 .getMetricsFeatureProvider() 161 .action(0 /* attribution */, 162 SettingsEnums.ACTION_PANEL_INTERACTION, 163 mMetricsCategory, 164 sliceLiveData.getValue().getUri().getLastPathSegment() 165 /* log key */, 166 eventInfo.actionType /* value */); 167 }) 168 ); 169 } 170 171 @Override isDividerAllowedAbove()172 public boolean isDividerAllowedAbove() { 173 return mDividerAllowedAbove; 174 } 175 176 @Override isDividerAllowedBelow()177 public boolean isDividerAllowedBelow() { 178 return mPanelFragment.getPanelViewType() != PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON; 179 } 180 } 181 } 182