1 /* 2 * Copyright 2016, 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 package com.android.managedprovisioning.preprovisioning.terms.adapters; 17 18 import static android.view.View.TEXT_ALIGNMENT_TEXT_START; 19 20 import static java.util.Objects.requireNonNull; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.ImageView; 28 import android.widget.LinearLayout; 29 import android.widget.TextView; 30 31 import androidx.recyclerview.widget.RecyclerView; 32 33 import com.android.managedprovisioning.R; 34 import com.android.managedprovisioning.common.AccessibilityContextMenuMaker; 35 import com.android.managedprovisioning.common.ClickableSpanFactory; 36 import com.android.managedprovisioning.common.StylerHelper; 37 import com.android.managedprovisioning.common.Utils; 38 import com.android.managedprovisioning.preprovisioning.terms.TermsDocument; 39 40 import com.google.android.setupdesign.util.DescriptionStyler; 41 42 import java.util.List; 43 44 /** 45 * Displays {@link TermsDocument} objects in a {@link RecyclerView}. 46 */ 47 public class TermsListAdapter extends RecyclerView.Adapter<TermsListAdapter.TermsViewHolder> { 48 private static final int VIEW_TYPE_GENERAL_DISCLAIMER = 1; 49 private static final int VIEW_TYPE_REGULAR_DISCLAIMER = 2; 50 51 private final TermsDocument mGenericDisclaimer; 52 private final List<TermsDocument> mTermsDocuments; 53 private final LayoutInflater mInflater; 54 private final AccessibilityContextMenuMaker mContextMenuMaker; 55 private final Context mContext; 56 private final TermsBridge mTermsBridge; 57 private final Utils mUtils; 58 private final StylerHelper mStylerHelper; 59 TermsListAdapter(Context context, TermsDocument genericDisclaimer, List<TermsDocument> termsDocuments, LayoutInflater layoutInflater, AccessibilityContextMenuMaker contextMenuMaker, TermsBridge termsBridge, Utils utils, StylerHelper stylerHelper)60 public TermsListAdapter(Context context, TermsDocument genericDisclaimer, 61 List<TermsDocument> termsDocuments, LayoutInflater layoutInflater, 62 AccessibilityContextMenuMaker contextMenuMaker, TermsBridge termsBridge, Utils utils, 63 StylerHelper stylerHelper) { 64 mGenericDisclaimer = requireNonNull(genericDisclaimer); 65 mTermsDocuments = requireNonNull(termsDocuments); 66 mInflater = requireNonNull(layoutInflater); 67 mContextMenuMaker = requireNonNull(contextMenuMaker); 68 mContext = requireNonNull(context); 69 mTermsBridge = requireNonNull(termsBridge); 70 mUtils = requireNonNull(utils); 71 mStylerHelper = requireNonNull(stylerHelper); 72 } 73 74 @Override onCreateViewHolder(ViewGroup parent, int viewType)75 public TermsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 76 View rootView = null; 77 switch (viewType) { 78 case VIEW_TYPE_GENERAL_DISCLAIMER: 79 rootView = createGeneralDisclaimerView(parent); 80 break; 81 case VIEW_TYPE_REGULAR_DISCLAIMER: 82 rootView = createRegularDisclaimerView(parent); 83 break; 84 } 85 return new TermsViewHolder(rootView); 86 } 87 createRegularDisclaimerView(ViewGroup parent)88 private View createRegularDisclaimerView(ViewGroup parent) { 89 View rootView = mInflater.inflate( 90 R.layout.terms_disclaimer_header, parent, /* attachToRoot= */ false); 91 mStylerHelper.applyListItemStyling(rootView, 92 new LinearLayout.LayoutParams(rootView.getLayoutParams())); 93 return rootView; 94 } 95 createGeneralDisclaimerView(ViewGroup parent)96 private View createGeneralDisclaimerView(ViewGroup parent) { 97 View rootView = mInflater.inflate( 98 R.layout.terms_disclaimer_general, parent, /* attachToRoot= */ false); 99 TextView textView = rootView.findViewById(R.id.terms_disclaimer_general); 100 DescriptionStyler.applyPartnerCustomizationHeavyStyle(textView); 101 textView.setTextAlignment(TEXT_ALIGNMENT_TEXT_START); 102 return rootView; 103 } 104 105 @Override onBindViewHolder(TermsViewHolder holder, int position)106 public void onBindViewHolder(TermsViewHolder holder, int position) { 107 int itemViewType = getItemViewType(position); 108 switch (itemViewType) { 109 case VIEW_TYPE_GENERAL_DISCLAIMER: 110 onBindGeneralDisclaimerViewHolder(holder); 111 break; 112 case VIEW_TYPE_REGULAR_DISCLAIMER: 113 onBindDisclaimerViewHolder(holder, /* disclaimerIndex= */ position - 1); 114 break; 115 } 116 } 117 onBindGeneralDisclaimerViewHolder(TermsViewHolder holder)118 private void onBindGeneralDisclaimerViewHolder(TermsViewHolder holder) { 119 holder.mGeneralDisclaimer.setText(mGenericDisclaimer.getContent()); 120 } 121 onBindDisclaimerViewHolder(TermsViewHolder holder, int disclaimerIndex)122 private void onBindDisclaimerViewHolder(TermsViewHolder holder, int disclaimerIndex) { 123 TermsDocument disclaimer = getDisclaimer(disclaimerIndex); 124 String heading = disclaimer.getHeading(); 125 setupRootView(disclaimerIndex, heading, holder); 126 setupDisclaimerHeading(heading, holder); 127 setupDisclaimerContent(disclaimer, holder); 128 setExpanded(isExpanded(disclaimerIndex), disclaimerIndex, holder); 129 } 130 131 @Override getItemViewType(int position)132 public int getItemViewType(int position) { 133 if (position == 0) { 134 return VIEW_TYPE_GENERAL_DISCLAIMER; 135 } 136 return VIEW_TYPE_REGULAR_DISCLAIMER; 137 } 138 setupRootView(int position, String heading, TermsViewHolder viewHolder)139 private void setupRootView(int position, String heading, TermsViewHolder viewHolder) { 140 viewHolder.itemView 141 .setContentDescription(mContext.getString(R.string.section_heading, heading)); 142 viewHolder.itemView.setOnClickListener(view -> 143 setExpanded(!isExpanded(position), position, viewHolder)); 144 } 145 setupDisclaimerHeading(String heading, TermsViewHolder viewHolder)146 private void setupDisclaimerHeading(String heading, TermsViewHolder viewHolder) { 147 viewHolder.mDisclaimerHeader.setText(heading); 148 } 149 setupDisclaimerContent(TermsDocument disclaimer, TermsViewHolder viewHolder)150 private void setupDisclaimerContent(TermsDocument disclaimer, TermsViewHolder viewHolder) { 151 TextView disclaimerContent = viewHolder.mDisclaimerContent; 152 TermsAdapterUtils.populateContentTextView( 153 mContext, disclaimerContent, disclaimer, new ClickableSpanFactory( 154 mUtils.getAccentColor(mContext), 155 mTermsBridge::onLinkClicked)); 156 mContextMenuMaker.registerWithActivity(disclaimerContent); 157 } 158 updateViewsForExpandedState(boolean expanded, TermsViewHolder viewHolder)159 private void updateViewsForExpandedState(boolean expanded, TermsViewHolder viewHolder) { 160 viewHolder.mDisclaimerContentContainer.setVisibility(expanded ? View.VISIBLE : View.GONE); 161 viewHolder.mChevron.setRotation(expanded ? 90 : -90); 162 } 163 164 @Override getItemCount()165 public int getItemCount() { 166 // First item is always the general disclaimer 167 return 1 + mTermsDocuments.size(); 168 } 169 getDisclaimer(int index)170 private TermsDocument getDisclaimer(int index) { 171 return mTermsDocuments.get(index); 172 } 173 isExpanded(int position)174 private Boolean isExpanded(int position) { 175 return mTermsBridge.isTermExpanded(position); 176 } 177 setExpanded(boolean expanded, int position, TermsViewHolder viewHolder)178 private void setExpanded(boolean expanded, int position, TermsViewHolder viewHolder) { 179 mTermsBridge.onTermExpanded(position, expanded); 180 updateViewsForExpandedState(expanded, viewHolder); 181 } 182 183 /** 184 * Used for communicating with the owner of the adapter. 185 */ 186 public interface TermsBridge { 187 /** 188 * Returns whether the disclaimer at position {@code position} is expanded. 189 */ isTermExpanded(int position)190 boolean isTermExpanded(int position); 191 192 /** 193 * Expands the disclaimer at position {@code position} if {@code expanded} is {@code true}, 194 * otherwise collapses it. 195 */ onTermExpanded(int position, boolean expanded)196 void onTermExpanded(int position, boolean expanded); 197 198 /** 199 * Callback invoked when a link is tapped from the terms content text. 200 * @param intent a web intent ready to be launched. 201 */ onLinkClicked(Intent intent)202 void onLinkClicked(Intent intent); 203 } 204 205 static class TermsViewHolder extends RecyclerView.ViewHolder { 206 final TextView mGeneralDisclaimer; 207 final TextView mDisclaimerHeader; 208 final TextView mDisclaimerContent; 209 final ViewGroup mDisclaimerContentContainer; 210 final ImageView mChevron; 211 TermsViewHolder(View itemView)212 TermsViewHolder(View itemView) { 213 super(itemView); 214 mGeneralDisclaimer = itemView.findViewById(R.id.terms_disclaimer_general); 215 mDisclaimerHeader = itemView.findViewById(R.id.sud_items_title); 216 mDisclaimerContent = itemView.findViewById(R.id.disclaimer_content); 217 mDisclaimerContentContainer = itemView.findViewById(R.id.disclaimer_content_container); 218 mChevron = itemView.findViewById(R.id.chevron); 219 } 220 } 221 } 222