• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.permissioncontroller.safetycenter.ui;
18 
19 import static android.os.Build.VERSION_CODES.TIRAMISU;
20 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
21 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
23 
24 import static java.util.Objects.requireNonNull;
25 
26 import android.app.AlertDialog;
27 import android.app.Dialog;
28 import android.content.Context;
29 import android.os.Bundle;
30 import android.safetycenter.SafetyCenterIssue;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.ViewGroup.MarginLayoutParams;
36 import android.widget.Button;
37 import android.widget.LinearLayout;
38 import android.widget.Space;
39 import android.widget.TextView;
40 
41 import androidx.annotation.ColorRes;
42 import androidx.annotation.Nullable;
43 import androidx.annotation.RequiresApi;
44 import androidx.appcompat.view.ContextThemeWrapper;
45 import androidx.core.content.ContextCompat;
46 import androidx.fragment.app.DialogFragment;
47 import androidx.fragment.app.FragmentManager;
48 import androidx.preference.Preference;
49 import androidx.preference.PreferenceViewHolder;
50 
51 import com.android.modules.utils.build.SdkLevel;
52 import com.android.permissioncontroller.R;
53 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
54 
55 import com.google.android.material.button.MaterialButton;
56 import com.google.android.material.shape.AbsoluteCornerSize;
57 import com.google.android.material.shape.CornerSize;
58 import com.google.android.material.shape.ShapeAppearanceModel;
59 
60 import java.util.Objects;
61 
62 /** A preference that displays a card representing a {@link SafetyCenterIssue}. */
63 @RequiresApi(TIRAMISU)
64 public class IssueCardPreference extends Preference implements ComparablePreference {
65 
66     public static final String TAG = IssueCardPreference.class.getSimpleName();
67 
68     private final IssueCardAnimator mIssueCardAnimator =
69             new IssueCardAnimator(this::markIssueResolvedUiCompleted);
70     private final SafetyCenterViewModel mSafetyCenterViewModel;
71     private final SafetyCenterIssue mIssue;
72     private final FragmentManager mDialogFragmentManager;
73     @Nullable private String mResolvedIssueActionId;
74     @Nullable private final Integer mTaskId;
75     private final boolean mIsDismissed;
76     private final PositionInCardList mPositionInCardList;
77 
IssueCardPreference( Context context, SafetyCenterViewModel safetyCenterViewModel, SafetyCenterIssue issue, @Nullable String resolvedIssueActionId, FragmentManager dialogFragmentManager, @Nullable Integer launchTaskId, boolean isDismissed, PositionInCardList positionInCardList)78     public IssueCardPreference(
79             Context context,
80             SafetyCenterViewModel safetyCenterViewModel,
81             SafetyCenterIssue issue,
82             @Nullable String resolvedIssueActionId,
83             FragmentManager dialogFragmentManager,
84             @Nullable Integer launchTaskId,
85             boolean isDismissed,
86             PositionInCardList positionInCardList) {
87         super(context);
88         setLayoutResource(R.layout.preference_issue_card);
89 
90         mSafetyCenterViewModel = requireNonNull(safetyCenterViewModel);
91         mIssue = requireNonNull(issue);
92         mDialogFragmentManager = dialogFragmentManager;
93         mResolvedIssueActionId = resolvedIssueActionId;
94         mTaskId = launchTaskId;
95         mIsDismissed = isDismissed;
96         mPositionInCardList = positionInCardList;
97     }
98 
99     @Override
onBindViewHolder(PreferenceViewHolder holder)100     public void onBindViewHolder(PreferenceViewHolder holder) {
101         super.onBindViewHolder(holder);
102 
103         holder.itemView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId());
104         int topMargin = getTopMargin(mPositionInCardList, getContext());
105         MarginLayoutParams layoutParams = (MarginLayoutParams) holder.itemView.getLayoutParams();
106         if (layoutParams.topMargin != topMargin) {
107             layoutParams.topMargin = topMargin;
108             holder.itemView.setLayoutParams(layoutParams);
109         }
110 
111         // Set default group visibility in case view is being reused
112         holder.findViewById(R.id.default_issue_content).setVisibility(View.VISIBLE);
113         holder.findViewById(R.id.resolved_issue_content).setVisibility(View.GONE);
114 
115         configureDismissButton(holder.findViewById(R.id.issue_card_dismiss_btn));
116 
117         TextView titleTextView = (TextView) holder.findViewById(R.id.issue_card_title);
118         titleTextView.setText(mIssue.getTitle());
119         ((TextView) holder.findViewById(R.id.issue_card_summary)).setText(mIssue.getSummary());
120 
121         TextView attributionTitleTextView =
122                 (TextView) holder.findViewById(R.id.issue_card_attribution_title);
123         maybeDisplayText(
124                 SdkLevel.isAtLeastU() ? mIssue.getAttributionTitle() : null,
125                 attributionTitleTextView);
126 
127         TextView subtitleTextView = (TextView) holder.findViewById(R.id.issue_card_subtitle);
128         maybeDisplayText(mIssue.getSubtitle(), subtitleTextView);
129 
130         holder.itemView.setClickable(false);
131 
132         configureContentDescription(attributionTitleTextView, titleTextView);
133         configureButtonList(holder);
134         configureSafetyProtectionView(holder);
135         maybeStartResolutionAnimation(holder);
136 
137         mSafetyCenterViewModel
138                 .getInteractionLogger()
139                 .recordIssueViewed(mIssue, mIsDismissed);
140     }
141 
maybeDisplayText(@ullable CharSequence maybeText, TextView textView)142     private void maybeDisplayText(@Nullable CharSequence maybeText, TextView textView) {
143         if (TextUtils.isEmpty(maybeText)) {
144             textView.setVisibility(View.GONE);
145         } else {
146             textView.setText(maybeText);
147             textView.setVisibility(View.VISIBLE);
148         }
149     }
150 
configureContentDescription( TextView attributionTitleTextView, TextView titleTextView)151     private void configureContentDescription(
152             TextView attributionTitleTextView, TextView titleTextView) {
153         TextView firstVisibleTextView;
154         if (attributionTitleTextView.getVisibility() == View.VISIBLE) {
155             // Attribution title might not be present for an issue, title always is.
156             firstVisibleTextView = attributionTitleTextView;
157 
158             // Clear the modified title description in case this view is reused.
159             titleTextView.setContentDescription(null);
160         } else {
161             firstVisibleTextView = titleTextView;
162         }
163 
164         // We would like to say "alert" before reading the content of the issue card. Best way to
165         // do that is by modifying the content description of the first view that would be read
166         // in the issue card.
167         firstVisibleTextView.setContentDescription(
168                 getContext()
169                         .getString(
170                                 R.string.safety_center_issue_card_prefix_content_description,
171                                 firstVisibleTextView.getText()));
172     }
173 
configureButtonList(PreferenceViewHolder holder)174     private void configureButtonList(PreferenceViewHolder holder) {
175         LinearLayout buttonList =
176                 ((LinearLayout) holder.findViewById(R.id.issue_card_action_button_list));
177         buttonList.removeAllViews(); // This view may be recycled from another issue
178 
179         for (int i = 0; i < mIssue.getActions().size(); i++) {
180             SafetyCenterIssue.Action action = mIssue.getActions().get(i);
181             ActionButtonBuilder builder =
182                     new ActionButtonBuilder(action, holder.itemView.getContext())
183                             .setIndex(i)
184                             .setActionButtonListSize(mIssue.getActions().size())
185                             .setIsDismissed(mIsDismissed)
186                             .setIsLargeScreen(buttonList instanceof EqualWidthContainer);
187             builder.buildAndAddToView(buttonList);
188         }
189     }
190 
getTopMargin(PositionInCardList position, Context context)191     private int getTopMargin(PositionInCardList position, Context context) {
192         switch (position) {
193             case LIST_START_END:
194             case LIST_START_CARD_END:
195                 return context.getResources()
196                         .getDimensionPixelSize(
197                                 mIsDismissed ? R.dimen.sc_card_margin : R.dimen.sc_spacing_large);
198             default:
199                 return position.getTopMargin(context);
200         }
201     }
202 
configureSafetyProtectionView(PreferenceViewHolder holder)203     private void configureSafetyProtectionView(PreferenceViewHolder holder) {
204         View safetyProtectionSectionView =
205                 holder.findViewById(R.id.issue_card_protected_by_android);
206         if (safetyProtectionSectionView.getVisibility() == View.GONE) {
207             holder.itemView.setPaddingRelative(
208                     holder.itemView.getPaddingStart(),
209                     holder.itemView.getPaddingTop(),
210                     holder.itemView.getPaddingEnd(),
211                     /* bottom= */ getContext()
212                             .getResources()
213                             .getDimensionPixelSize(R.dimen.sc_card_margin_bottom));
214         } else {
215             holder.itemView.setPaddingRelative(
216                     holder.itemView.getPaddingStart(),
217                     holder.itemView.getPaddingTop(),
218                     holder.itemView.getPaddingEnd(),
219                     /* bottom= */ 0);
220         }
221     }
222 
maybeStartResolutionAnimation(PreferenceViewHolder holder)223     private void maybeStartResolutionAnimation(PreferenceViewHolder holder) {
224         if (mResolvedIssueActionId == null) {
225             return;
226         }
227 
228         for (SafetyCenterIssue.Action action : mIssue.getActions()) {
229             if (action.getId().equals(mResolvedIssueActionId)) {
230                 mIssueCardAnimator.transitionToIssueResolvedThenMarkComplete(
231                         getContext(), holder, action);
232             }
233         }
234     }
235 
getSeverityLevel()236     public int getSeverityLevel() {
237         return mIssue.getSeverityLevel();
238     }
239 
configureDismissButton(View dismissButton)240     private void configureDismissButton(View dismissButton) {
241         if (mIssue.isDismissible() && !mIsDismissed) {
242             dismissButton.setOnClickListener(
243                     mIssue.shouldConfirmDismissal()
244                             ? new ConfirmDismissalOnClickListener()
245                             : new DismissOnClickListener());
246             dismissButton.setVisibility(View.VISIBLE);
247 
248             SafetyCenterTouchTarget.configureSize(
249                     dismissButton, R.dimen.sc_icon_button_touch_target_size);
250         } else {
251             dismissButton.setVisibility(View.GONE);
252         }
253     }
254 
255     @Override
isSameItem(Preference preference)256     public boolean isSameItem(Preference preference) {
257         return (preference instanceof IssueCardPreference)
258                 && TextUtils.equals(
259                         mIssue.getId(), ((IssueCardPreference) preference).mIssue.getId());
260     }
261 
262     @Override
hasSameContents(Preference preference)263     public boolean hasSameContents(Preference preference) {
264         return (preference instanceof IssueCardPreference)
265                 && mIssue.equals(((IssueCardPreference) preference).mIssue)
266                 && Objects.equals(
267                         mResolvedIssueActionId,
268                         ((IssueCardPreference) preference).mResolvedIssueActionId)
269                 && mIsDismissed == ((IssueCardPreference) preference).mIsDismissed
270                 && mPositionInCardList == ((IssueCardPreference) preference).mPositionInCardList;
271     }
272 
273     private class DismissOnClickListener implements View.OnClickListener {
274         @Override
onClick(View v)275         public void onClick(View v) {
276             mSafetyCenterViewModel.dismissIssue(mIssue);
277             mSafetyCenterViewModel
278                     .getInteractionLogger()
279                     .recordForIssue(Action.ISSUE_DISMISS_CLICKED, mIssue, mIsDismissed);
280         }
281     }
282 
283     private class ConfirmDismissalOnClickListener implements View.OnClickListener {
284         @Override
onClick(View v)285         public void onClick(View v) {
286             ConfirmDismissalDialogFragment.newInstance(mIssue)
287                     .showNow(mDialogFragmentManager, /* tag= */ null);
288         }
289     }
290 
291     /** Fragment to display a dismissal confirmation dialog for an {@link IssueCardPreference}. */
292     public static class ConfirmDismissalDialogFragment extends DialogFragment {
293         private static final String ISSUE_KEY = "confirm_dialog_sc_issue";
294 
newInstance(SafetyCenterIssue issue)295         private static ConfirmDismissalDialogFragment newInstance(SafetyCenterIssue issue) {
296             ConfirmDismissalDialogFragment fragment = new ConfirmDismissalDialogFragment();
297 
298             Bundle args = new Bundle();
299             args.putParcelable(ISSUE_KEY, issue);
300             fragment.setArguments(args);
301 
302             return fragment;
303         }
304 
305         @Override
onCreateDialog(@ullable Bundle savedInstanceState)306         public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
307             SafetyCenterViewModel safetyCenterViewModel =
308                     ((SafetyCenterFragment) requireParentFragment()).getSafetyCenterViewModel();
309             SafetyCenterIssue issue =
310                     requireNonNull(
311                             requireArguments().getParcelable(ISSUE_KEY, SafetyCenterIssue.class));
312             return new AlertDialog.Builder(getContext())
313                     .setTitle(R.string.safety_center_issue_card_dismiss_confirmation_title)
314                     .setMessage(R.string.safety_center_issue_card_dismiss_confirmation_message)
315                     .setPositiveButton(
316                             R.string.safety_center_issue_card_confirm_dismiss_button,
317                             (dialog, which) -> {
318                                 safetyCenterViewModel.dismissIssue(issue);
319                                 safetyCenterViewModel
320                                         .getInteractionLogger()
321                                         .recordForIssue(
322                                                 Action.ISSUE_DISMISS_CLICKED,
323                                                 issue,
324                                                 // You can only dismiss non-dismissed issues
325                                                 /* isDismissed= */ false);
326                             })
327                     .setNegativeButton(
328                             R.string.safety_center_issue_card_cancel_dismiss_button, null)
329                     .create();
330         }
331     }
332 
333     /** A dialog to prompt for a confirmation to performn an Action. */
334     @RequiresApi(UPSIDE_DOWN_CAKE)
335     public static class ConfirmActionDialogFragment extends DialogFragment {
336         private static final String ISSUE_KEY = "issue";
337         private static final String ACTION_KEY = "action";
338         private static final String TASK_ID_KEY = "taskId";
339         private static final String IS_PRIMARY_BUTTON_KEY = "isPrimaryButton";
340         private static final String IS_DISMISSED_KEY = "isDismissed";
341 
342         /** Create new fragment with the data it will need. */
newInstance( SafetyCenterIssue issue, SafetyCenterIssue.Action action, @Nullable Integer taskId, boolean isFirstButton, boolean isDismissed)343         public static ConfirmActionDialogFragment newInstance(
344                 SafetyCenterIssue issue,
345                 SafetyCenterIssue.Action action,
346                 @Nullable Integer taskId,
347                 boolean isFirstButton,
348                 boolean isDismissed) {
349             ConfirmActionDialogFragment fragment = new ConfirmActionDialogFragment();
350 
351             Bundle args = new Bundle();
352             args.putParcelable(ISSUE_KEY, issue);
353             args.putParcelable(ACTION_KEY, action);
354             args.putBoolean(IS_PRIMARY_BUTTON_KEY, isFirstButton);
355             args.putBoolean(IS_DISMISSED_KEY, isDismissed);
356 
357             if (taskId != null) {
358                 args.putInt(TASK_ID_KEY, taskId);
359             }
360 
361             fragment.setArguments(args);
362 
363             return fragment;
364         }
365 
366         @Override
onCreateDialog(Bundle savedInstanceState)367         public Dialog onCreateDialog(Bundle savedInstanceState) {
368             SafetyCenterViewModel safetyCenterViewModel =
369                     ((SafetyCenterFragment) requireParentFragment()).getSafetyCenterViewModel();
370             SafetyCenterIssue issue =
371                     requireNonNull(
372                             requireArguments().getParcelable(ISSUE_KEY, SafetyCenterIssue.class));
373             SafetyCenterIssue.Action action =
374                     requireNonNull(
375                             requireArguments()
376                                     .getParcelable(ACTION_KEY, SafetyCenterIssue.Action.class));
377             boolean isPrimaryButton = requireArguments().getBoolean(IS_PRIMARY_BUTTON_KEY);
378             boolean isDismissed = requireArguments().getBoolean(IS_DISMISSED_KEY);
379 
380             Integer taskId =
381                     requireArguments().containsKey(TASK_ID_KEY)
382                             ? requireArguments().getInt(TASK_ID_KEY)
383                             : null;
384 
385             return new AlertDialog.Builder(getContext())
386                     .setTitle(action.getConfirmationDialogDetails().getTitle())
387                     .setMessage(action.getConfirmationDialogDetails().getText())
388                     .setPositiveButton(
389                             action.getConfirmationDialogDetails().getAcceptButtonText(),
390                             (dialog, which) -> {
391                                 safetyCenterViewModel.executeIssueAction(issue, action, taskId);
392                                 // TODO(b/269097766): Is this the best logging model?
393                                 safetyCenterViewModel
394                                         .getInteractionLogger()
395                                         .recordForIssue(
396                                                 isPrimaryButton
397                                                         ? Action.ISSUE_PRIMARY_ACTION_CLICKED
398                                                         : Action.ISSUE_SECONDARY_ACTION_CLICKED,
399                                                 issue,
400                                                 isDismissed);
401                             })
402                     .setNegativeButton(
403                             action.getConfirmationDialogDetails().getDenyButtonText(), null)
404                     .create();
405         }
406     }
407 
408     private void markIssueResolvedUiCompleted() {
409         if (mResolvedIssueActionId != null) {
410             mResolvedIssueActionId = null;
411             mSafetyCenterViewModel.markIssueResolvedUiCompleted(mIssue.getId());
412         }
413     }
414 
415     private class ActionButtonBuilder {
416         private final SafetyCenterIssue.Action mAction;
417         private final Context mContext;
418         private final ContextThemeWrapper mContextThemeWrapper;
419         private int mIndex;
420         private int mActionButtonListSize;
421         private boolean mIsDismissed = false;
422         private boolean mIsLargeScreen = false;
423 
424         ActionButtonBuilder(SafetyCenterIssue.Action action, Context context) {
425             mAction = action;
426             mContext = context;
427             mContextThemeWrapper =
428                     new ContextThemeWrapper(context, R.style.Theme_MaterialComponents_DayNight);
429         }
430 
431         public ActionButtonBuilder setIndex(int index) {
432             mIndex = index;
433             return this;
434         }
435 
436         public ActionButtonBuilder setActionButtonListSize(int actionButtonListSize) {
437             mActionButtonListSize = actionButtonListSize;
438             return this;
439         }
440 
441         public ActionButtonBuilder setIsDismissed(boolean isDismissed) {
442             mIsDismissed = isDismissed;
443             return this;
444         }
445 
446         public ActionButtonBuilder setIsLargeScreen(boolean isLargeScreen) {
447             mIsLargeScreen = isLargeScreen;
448             return this;
449         }
450 
451         private boolean isPrimaryButton() {
452             return mIndex == 0;
453         }
454 
455         private boolean isLastButton() {
456             return mIndex == (mActionButtonListSize - 1);
457         }
458 
459         private boolean isFilled() {
460             return isPrimaryButton() && !mIsDismissed;
461         }
462 
463         public void buildAndAddToView(LinearLayout buttonList) {
464             MaterialButton button = new MaterialButton(mContextThemeWrapper, null, getStyle());
465             if (SdkLevel.isAtLeastU() && !mIsLargeScreen) {
466                 configureGroupStyleCorners(button);
467             }
468             setButtonColors(button);
469             setButtonLayout(button);
470             button.setText(mAction.getLabel());
471             button.setEnabled(!mAction.isInFlight());
472             button.setOnClickListener(
473                     view -> {
474                         if (SdkLevel.isAtLeastU()
475                                 && mAction.getConfirmationDialogDetails() != null) {
476                             ConfirmActionDialogFragment.newInstance(
477                                             mIssue,
478                                             mAction,
479                                             mTaskId,
480                                             isPrimaryButton(),
481                                             mIsDismissed)
482                                     .showNow(mDialogFragmentManager, /* tag= */ null);
483                         } else {
484                             if (mAction.willResolve()) {
485                                 // Without a confirmation, the button remains tappable. Disable the
486                                 // button to prevent double-taps.
487                                 // We ideally want to do this on any button press, however out of an
488                                 // abundance of caution we only do it with actions that indicate
489                                 // they will resolve (and therefore we can rely on a model update to
490                                 // redraw state - either to isInFlight() or simply resolving the
491                                 // issue.
492                                 button.setEnabled(false);
493                             }
494                             mSafetyCenterViewModel.executeIssueAction(mIssue, mAction, mTaskId);
495                             mSafetyCenterViewModel
496                                     .getInteractionLogger()
497                                     .recordForIssue(
498                                             isPrimaryButton()
499                                                     ? Action.ISSUE_PRIMARY_ACTION_CLICKED
500                                                     : Action.ISSUE_SECONDARY_ACTION_CLICKED,
501                                             mIssue,
502                                             mIsDismissed);
503                         }
504                     });
505 
506             maybeAddSpaceToView(buttonList);
507             buttonList.addView(button);
508         }
509 
510         /**
511          * Configures "group-style" corners for this button, where the first button in the list has
512          * large corners on top and the last button in the list has large corners on bottom.
513          */
514         @RequiresApi(UPSIDE_DOWN_CAKE)
515         private void configureGroupStyleCorners(MaterialButton button) {
516             button.setCornerRadiusResource(R.dimen.sc_button_corner_radius_small);
517             ShapeAppearanceModel.Builder shapeAppearanceModelBuilder =
518                     button.getShapeAppearanceModel().toBuilder();
519 
520             CornerSize largeCornerSize =
521                     new AbsoluteCornerSize(
522                             mContext.getResources()
523                                     .getDimensionPixelSize(R.dimen.sc_button_corner_radius));
524             if (isPrimaryButton()) {
525                 shapeAppearanceModelBuilder
526                         .setTopLeftCornerSize(largeCornerSize)
527                         .setTopRightCornerSize(largeCornerSize);
528             }
529             if (isLastButton()) {
530                 shapeAppearanceModelBuilder
531                         .setBottomLeftCornerSize(largeCornerSize)
532                         .setBottomRightCornerSize(largeCornerSize);
533             }
534 
535             button.setShapeAppearanceModel(shapeAppearanceModelBuilder.build());
536         }
537 
538         private void maybeAddSpaceToView(LinearLayout buttonList) {
539             if (isPrimaryButton()) {
540                 return;
541             }
542 
543             int margin =
544                     mContext.getResources()
545                             .getDimensionPixelSize(R.dimen.sc_action_button_list_margin);
546             Space space = new Space(mContext);
547             space.setLayoutParams(new ViewGroup.LayoutParams(margin, margin));
548             buttonList.addView(space);
549         }
550 
551         private int getStyle() {
552             return isFilled() ? R.attr.scActionButtonStyle : R.attr.scSecondaryActionButtonStyle;
553         }
554 
555         private void setButtonColors(MaterialButton button) {
556             if (isFilled()) {
557                 button.setBackgroundTintList(
558                         ContextCompat.getColorStateList(
559                                 mContext,
560                                 getPrimaryButtonColorFromSeverity(mIssue.getSeverityLevel())));
561             } else {
562                 button.setStrokeColor(
563                         ContextCompat.getColorStateList(
564                                 mContext,
565                                 getSecondaryButtonStrokeColorFromSeverity(
566                                         mIssue.getSeverityLevel())));
567             }
568         }
569 
570         private void setButtonLayout(Button button) {
571             MarginLayoutParams layoutParams = new MarginLayoutParams(layoutWidth(), WRAP_CONTENT);
572             button.setLayoutParams(layoutParams);
573         }
574 
575         private int layoutWidth() {
576             if (mIsLargeScreen) {
577                 return WRAP_CONTENT;
578             } else {
579                 return MATCH_PARENT;
580             }
581         }
582 
583         @ColorRes
584         private int getPrimaryButtonColorFromSeverity(int issueSeverityLevel) {
585             return pickColorForSeverityLevel(
586                     issueSeverityLevel,
587                     R.color.safety_center_button_info,
588                     R.color.safety_center_button_recommend,
589                     R.color.safety_center_button_warn);
590         }
591 
592         @ColorRes
593         private int getSecondaryButtonStrokeColorFromSeverity(int issueSeverityLevel) {
594             return pickColorForSeverityLevel(
595                     issueSeverityLevel,
596                     R.color.safety_center_outline_button_info,
597                     R.color.safety_center_outline_button_recommend,
598                     R.color.safety_center_outline_button_warn);
599         }
600 
601         @ColorRes
602         private int pickColorForSeverityLevel(
603                 int issueSeverityLevel,
604                 @ColorRes int infoColor,
605                 @ColorRes int recommendColor,
606                 @ColorRes int warnColor) {
607             switch (issueSeverityLevel) {
608                 case SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK:
609                     return infoColor;
610                 case SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATION:
611                     return recommendColor;
612                 case SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING:
613                     return warnColor;
614                 default:
615                     Log.w(
616                             TAG,
617                             String.format("Unexpected issueSeverityLevel: %s", issueSeverityLevel));
618                     return infoColor;
619             }
620         }
621     }
622 }
623