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