diff options
author | Tyler Dewey <deweytyl@google.com> | 2023-03-08 11:33:12 +0000 |
---|---|---|
committer | Tyler Dewey <deweytyl@google.com> | 2023-03-13 13:42:35 +0000 |
commit | 33daeba42d7fc94e48a585e3afca457466814e84 (patch) | |
tree | dd8b4ffc00c38475da934702183e2fec9d8b44ca /PermissionController/src/com/android/permissioncontroller/safetycenter | |
parent | 031ed668b811efc948ecd2c1ff2f48ebc543eab4 (diff) | |
download | Permission-33daeba42d7fc94e48a585e3afca457466814e84.tar.gz |
Extract status card view and clean up button logic.
Bug: 269725572
Test: atest CtsSafetyCenterTestCases SafetyCenterFunctionalTestCases
Change-Id: Ia79d13b4ac770e864724ff6fb16f9df6656fda92
Diffstat (limited to 'PermissionController/src/com/android/permissioncontroller/safetycenter')
3 files changed, 181 insertions, 112 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java index 6c1c7aa90..61b36fc60 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java @@ -40,8 +40,7 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; - -import com.google.android.material.button.MaterialButton; +import com.android.permissioncontroller.safetycenter.ui.view.StatusCardView; import kotlin.Pair; @@ -81,68 +80,68 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } Context context = getContext(); - ImageView statusImage = (ImageView) holder.findViewById(R.id.status_image); - MaterialButton rescanButton = (MaterialButton) holder.findViewById(R.id.rescan_button); - MaterialButton pendingActionsRescanButton = - (MaterialButton) holder.findViewById(R.id.pending_actions_rescan_button); - View reviewSettingsButton = holder.findViewById(R.id.review_settings_button); - if (mStatus.hasPendingActions()) { - reviewSettingsButton.setOnClickListener( - l -> { - requireViewModel() - .navigateToSafetyCenter( - context, NavigationSource.QUICK_SETTINGS_TILE); - requireViewModel() - .getInteractionLogger() - .record(Action.REVIEW_SETTINGS_CLICKED); - }); - reviewSettingsButton.setVisibility(View.VISIBLE); - } else { - reviewSettingsButton.setVisibility(View.GONE); - } - rescanButton = updateRescanButtonUi(rescanButton, pendingActionsRescanButton); - setRescanButtonState(rescanButton); - - holder.findViewById(R.id.status_title_and_summary) + StatusCardView statusCardView = (StatusCardView) holder.itemView; + configureButtons(context, statusCardView); + statusCardView + .getTitleAndSummaryContainerView() .setContentDescription(mStatus.getContentDescription(context)); - rescanButton.setOnClickListener( - unused -> { - SafetyCenterViewModel viewModel = requireViewModel(); - viewModel.rescan(); - viewModel.getInteractionLogger().record(Action.SCAN_INITIATED); - }); - - updateStatusIcon(statusImage, rescanButton); + updateStatusIcon(statusCardView); - TextView titleTextView = (TextView) holder.findViewById(R.id.status_title); - TextView summaryTextView = (TextView) holder.findViewById(R.id.status_summary); - updateStatusText(titleTextView, summaryTextView); + updateStatusText(statusCardView.getTitleView(), statusCardView.getSummaryView()); - configureSafetyProtectionView(holder, context); + configureSafetyProtectionView(statusCardView, context); mFirstBind = false; } - private void configureSafetyProtectionView(PreferenceViewHolder holder, Context context) { - View safetyProtectionSectionView = holder.findViewById(R.id.safety_protection_section_view); + private void configureButtons(Context context, StatusCardView statusCardView) { + statusCardView + .getRescanButton() + .setOnClickListener( + unused -> { + SafetyCenterViewModel viewModel = requireViewModel(); + viewModel.rescan(); + viewModel.getInteractionLogger().record(Action.SCAN_INITIATED); + }); + statusCardView + .getReviewSettingsButton() + .setOnClickListener( + unused -> { + SafetyCenterViewModel viewModel = requireViewModel(); + viewModel.navigateToSafetyCenter( + context, NavigationSource.QUICK_SETTINGS_TILE); + viewModel.getInteractionLogger().record(Action.REVIEW_SETTINGS_CLICKED); + }); + + updateButtonState(statusCardView); + } + + private void updateButtonState(StatusCardView statusCardView) { + if (mStatus == null) return; // Shouldn't happen in practice but we do it for null safety. + statusCardView.showButtons(mStatus); + } + + private void configureSafetyProtectionView(StatusCardView statusCardView, Context context) { + View safetyProtectionSectionView = + statusCardView.findViewById(R.id.safety_protection_section_view); if (KotlinUtils.INSTANCE.shouldShowSafetyProtectionResources(context)) { // Hide the Safety Protection branding if there are any issue cards safetyProtectionSectionView.setVisibility( mStatus.hasIssues() ? View.GONE : View.VISIBLE); } if (safetyProtectionSectionView.getVisibility() == View.GONE) { - holder.itemView.setPaddingRelative( - holder.itemView.getPaddingStart(), - holder.itemView.getPaddingTop(), - holder.itemView.getPaddingEnd(), + statusCardView.setPaddingRelative( + statusCardView.getPaddingStart(), + statusCardView.getPaddingTop(), + statusCardView.getPaddingEnd(), /* bottom= */ getContext() .getResources() .getDimensionPixelSize(R.dimen.sc_card_margin_bottom)); } else { - holder.itemView.setPaddingRelative( - holder.itemView.getPaddingStart(), - holder.itemView.getPaddingTop(), - holder.itemView.getPaddingEnd(), + statusCardView.setPaddingRelative( + statusCardView.getPaddingStart(), + statusCardView.getPaddingTop(), + statusCardView.getPaddingEnd(), /* bottom= */ 0); } } @@ -155,14 +154,13 @@ public class SafetyStatusPreference extends Preference implements ComparablePref runTextAnimationIfNeeded(title, summary); } - private void updateStatusIcon(ImageView statusImage, View rescanButton) { + private void updateStatusIcon(StatusCardView statusCardView) { int severityLevel = mStatus.getSeverityLevel(); boolean isRefreshing = mStatus.isRefreshInProgress(); handleAnimationSequencerAction( mSequencer.onUpdateReceived(isRefreshing, severityLevel), - statusImage, - rescanButton, + statusCardView, /* scanningAnimation= */ null); } @@ -191,8 +189,9 @@ public class SafetyStatusPreference extends Preference implements ComparablePref } } - private void startScanningAnimation(ImageView statusImage, View rescanButton) { + private void startScanningAnimation(StatusCardView statusCardView) { mSequencer.onStartScanningAnimationStart(); + ImageView statusImage = statusCardView.getStatusImageView(); statusImage.setImageResource( StatusAnimationResolver.getScanningStartAnimation( mSequencer.getCurrentlyVisibleSeverityLevel())); @@ -203,15 +202,16 @@ public class SafetyStatusPreference extends Preference implements ComparablePref public void onAnimationEnd(Drawable drawable) { handleAnimationSequencerAction( mSequencer.onStartScanningAnimationEnd(), - statusImage, - rescanButton, + statusCardView, /* scanningAnimation= */ null); } }); animation.start(); } - private void continueScanningAnimation(ImageView statusImage, View rescanButton) { + private void continueScanningAnimation(StatusCardView statusCardView) { + ImageView statusImage = statusCardView.getStatusImageView(); + // clear previous scan animation in case we need to continue with different severity level Drawable statusDrawable = statusImage.getDrawable(); if (statusDrawable instanceof AnimatedVectorDrawable) { @@ -229,25 +229,25 @@ public class SafetyStatusPreference extends Preference implements ComparablePref handleAnimationSequencerAction( mSequencer.onContinueScanningAnimationEnd( mStatus.isRefreshInProgress(), mStatus.getSeverityLevel()), - statusImage, - rescanButton, + statusCardView, scanningAnim); } }); scanningAnim.start(); } - private void endScanningAnimation(ImageView statusImage, View rescanButton) { + private void endScanningAnimation(StatusCardView statusCardView) { + ImageView statusImage = statusCardView.getStatusImageView(); Drawable statusDrawable = statusImage.getDrawable(); int finishingSeverityLevel = mStatus.getSeverityLevel(); if (!(statusDrawable instanceof AnimatedVectorDrawable)) { - finishScanAnimation(statusImage, rescanButton, finishingSeverityLevel); + finishScanAnimation(statusCardView, finishingSeverityLevel); return; } AnimatedVectorDrawable animatedStatusDrawable = (AnimatedVectorDrawable) statusDrawable; if (!animatedStatusDrawable.isRunning()) { - finishScanAnimation(statusImage, rescanButton, finishingSeverityLevel); + finishScanAnimation(statusCardView, finishingSeverityLevel); return; } @@ -267,8 +267,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref @Override public void onAnimationEnd(Drawable drawable) { super.onAnimationEnd(drawable); - finishScanAnimation( - statusImage, rescanButton, finishingSeverityLevel); + finishScanAnimation(statusCardView, finishingSeverityLevel); } }); animatedDrawable.start(); @@ -276,18 +275,16 @@ public class SafetyStatusPreference extends Preference implements ComparablePref }); } - private void finishScanAnimation( - ImageView statusImage, View rescanButton, int finishedSeverityLevel) { - setRescanButtonState(rescanButton); + private void finishScanAnimation(StatusCardView statusCardView, int finishedSeverityLevel) { + updateButtonState(statusCardView); handleAnimationSequencerAction( mSequencer.onFinishScanAnimationEnd( mStatus.isRefreshInProgress(), finishedSeverityLevel), - statusImage, - rescanButton, + statusCardView, /* scanningAnimation= */ null); } - private void startIconChangeAnimation(ImageView statusImage, View rescanButton) { + private void startIconChangeAnimation(StatusCardView statusCardView) { int finalSeverityLevel = mStatus.getSeverityLevel(); int changeAnimationResId = StatusAnimationResolver.getStatusChangeAnimation( @@ -296,14 +293,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref handleAnimationSequencerAction( mSequencer.onCouldNotStartIconChangeAnimation( mStatus.isRefreshInProgress(), finalSeverityLevel), - statusImage, - rescanButton, + statusCardView, /* scanningAnimation= */ null); return; } mSequencer.onIconChangeAnimationStart(); - statusImage.setImageResource(changeAnimationResId); - AnimatedVectorDrawable animation = (AnimatedVectorDrawable) statusImage.getDrawable(); + statusCardView.getStatusImageView().setImageResource(changeAnimationResId); + AnimatedVectorDrawable animation = + (AnimatedVectorDrawable) statusCardView.getStatusImageView().getDrawable(); animation.clearAnimationCallbacks(); animation.registerAnimationCallback( new Animatable2.AnimationCallback() { @@ -312,8 +309,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref handleAnimationSequencerAction( mSequencer.onIconChangeAnimationEnd( mStatus.isRefreshInProgress(), finalSeverityLevel), - statusImage, - rescanButton, + statusCardView, /* scanningAnimation= */ null); } }); @@ -322,62 +318,47 @@ public class SafetyStatusPreference extends Preference implements ComparablePref private void handleAnimationSequencerAction( @Nullable SafetyStatusAnimationSequencer.Action action, - ImageView statusImage, - View rescanButton, + StatusCardView statusCardView, @Nullable AnimatedVectorDrawable scanningAnimation) { if (action == null) { return; } switch (action) { case START_SCANNING_ANIMATION: - startScanningAnimation(statusImage, rescanButton); + startScanningAnimation(statusCardView); break; case CONTINUE_SCANNING_ANIMATION: if (scanningAnimation != null) { scanningAnimation.start(); } else { - continueScanningAnimation(statusImage, rescanButton); + continueScanningAnimation(statusCardView); } break; case RESET_SCANNING_ANIMATION: - continueScanningAnimation(statusImage, rescanButton); + continueScanningAnimation(statusCardView); break; case FINISH_SCANNING_ANIMATION: - endScanningAnimation(statusImage, rescanButton); + endScanningAnimation(statusCardView); break; case START_ICON_CHANGE_ANIMATION: - startIconChangeAnimation(statusImage, rescanButton); + startIconChangeAnimation(statusCardView); break; case CHANGE_ICON_WITHOUT_ANIMATION: - setSettledStatus(statusImage); + setSettledStatus(statusCardView); break; } } - private void setSettledStatus(ImageView statusImage) { - Drawable statusDrawable = statusImage.getDrawable(); + private void setSettledStatus(StatusCardView statusCardView) { + Drawable statusDrawable = statusCardView.getStatusImageView().getDrawable(); if (statusDrawable instanceof AnimatedVectorDrawable) { ((AnimatedVectorDrawable) statusDrawable).clearAnimationCallbacks(); } - statusImage.setImageResource( - StatusUiData.Companion.getStatusImageResId( - mSequencer.getCurrentlyVisibleSeverityLevel())); - } - - /** - * Updates UI for the rescan button depending on the pending actions state and returns the - * correctly styled rescan button - */ - private MaterialButton updateRescanButtonUi( - MaterialButton rescanButton, MaterialButton pendingActionsRescanButton) { - if (mStatus.hasPendingActions()) { - rescanButton.setVisibility(View.GONE); - pendingActionsRescanButton.setVisibility(View.VISIBLE); - return pendingActionsRescanButton; - } - pendingActionsRescanButton.setVisibility(View.GONE); - rescanButton.setVisibility(View.VISIBLE); - return rescanButton; + statusCardView + .getStatusImageView() + .setImageResource( + StatusUiData.Companion.getStatusImageResId( + mSequencer.getCurrentlyVisibleSeverityLevel())); } void setData(StatusUiData statusUiData) { @@ -393,11 +374,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref return Objects.requireNonNull(mViewModel); } - private void setRescanButtonState(View rescanButton) { - rescanButton.setVisibility(mStatus.shouldShowRescanButton() ? View.VISIBLE : View.GONE); - rescanButton.setEnabled(!mStatus.isRefreshInProgress()); - } - // Calling notifyChanged while recyclerview is scrolling or computing layout will result in an // IllegalStateException. Post to handler to wait for UI to settle. private void safeNotifyChanged() { diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt index d83abbe12..741745c19 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt @@ -4,6 +4,8 @@ import android.content.Context import android.os.Build.VERSION_CODES.TIRAMISU import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterStatus +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN import android.util.Log import androidx.annotation.RequiresApi import com.android.permissioncontroller.R @@ -28,8 +30,8 @@ data class StatusUiData( private val TAG: String = StatusUiData::class.java.simpleName fun getStatusImageResId(severityLevel: Int) = when (severityLevel) { - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN, - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK -> R.drawable.safety_status_info + OVERALL_SEVERITY_LEVEL_UNKNOWN, + OVERALL_SEVERITY_LEVEL_OK -> R.drawable.safety_status_info SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION -> R.drawable.safety_status_recommendation SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> @@ -61,7 +63,8 @@ data class StatusUiData( return context.getString( R.string.safety_status_preference_title_and_summary_content_description, title, - getSummary(context)) + getSummary(context) + ) } val isRefreshInProgress: Boolean @@ -76,9 +79,23 @@ data class StatusUiData( return !hasIssues && !hasPendingActions && when (severityLevel) { - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK, - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN -> true + OVERALL_SEVERITY_LEVEL_OK, + OVERALL_SEVERITY_LEVEL_UNKNOWN -> true else -> false } } + + enum class ButtonToShow { + RESCAN, + REVIEW_SETTINGS + } + val buttonToShow: ButtonToShow? + get() = + when { + hasIssues -> null + hasPendingActions -> ButtonToShow.REVIEW_SETTINGS + severityLevel == OVERALL_SEVERITY_LEVEL_OK || + severityLevel == OVERALL_SEVERITY_LEVEL_UNKNOWN -> ButtonToShow.RESCAN + else -> null + } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt new file mode 100644 index 000000000..eef9a45b6 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.safetycenter.ui.view + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.RequiresApi +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.widget.SafetyProtectionSectionView +import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData +import com.google.android.material.button.MaterialButton + +@RequiresApi(Build.VERSION_CODES.TIRAMISU) +internal class StatusCardView +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { + + init { + inflate(context, R.layout.view_status_card, this) + } + + val statusImageView: ImageView by lazy { findViewById(R.id.status_image) } + val titleAndSummaryContainerView: LinearLayout by lazy { + findViewById(R.id.status_title_and_summary) + } + val titleView: TextView by lazy { findViewById(R.id.status_title) } + val summaryView: TextView by lazy { findViewById(R.id.status_summary) } + val reviewSettingsButton: MaterialButton by lazy { findViewById(R.id.review_settings_button) } + val rescanButton: MaterialButton by lazy { findViewById(R.id.rescan_button) } + val safetyProtectionSectionView: SafetyProtectionSectionView by lazy { + findViewById(R.id.safety_protection_section_view) + } + + fun showButtons(statusUiData: StatusUiData) { + rescanButton.isEnabled = !statusUiData.isRefreshInProgress + + when (statusUiData.buttonToShow) { + StatusUiData.ButtonToShow.RESCAN -> { + rescanButton.visibility = VISIBLE + reviewSettingsButton.visibility = GONE + } + StatusUiData.ButtonToShow.REVIEW_SETTINGS -> { + rescanButton.visibility = GONE + reviewSettingsButton.visibility = VISIBLE + } + null -> { + rescanButton.visibility = GONE + reviewSettingsButton.visibility = GONE + } + } + } +} |