summaryrefslogtreecommitdiff
path: root/PermissionController/src/com/android/permissioncontroller/safetycenter
diff options
context:
space:
mode:
authorYuri Ufimtsev <yufimtsev@google.com>2023-05-12 15:09:17 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-05-12 15:09:17 +0000
commit9e22e62df20e43ea64aa5a2608f38071fd0ed0ef (patch)
treefdb85ecd0eb2a3d6ab2203560f92911cafd0aaf6 /PermissionController/src/com/android/permissioncontroller/safetycenter
parentcea865007a8690a6cb288e8befb7906f5162fb38 (diff)
parent134aee7d974abee0e38e2e89aa2340b077136990 (diff)
downloadPermission-9e22e62df20e43ea64aa5a2608f38071fd0ed0ef.tar.gz
Merge "Allow SafetyCenter Subpages to scroll up after collapsing More issues" into udc-dev
Diffstat (limited to 'PermissionController/src/com/android/permissioncontroller/safetycenter')
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt129
3 files changed, 136 insertions, 1 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index dd90fd559..940cb2f69 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -41,6 +41,7 @@ import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
@@ -65,6 +66,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
private static final String ISSUES_GROUP_KEY = "issues_group";
private static final String ENTRIES_GROUP_KEY = "entries_group";
private static final String STATIC_ENTRIES_GROUP_KEY = "static_entries_group";
+ private static final String SPACER_KEY = "spacer";
private SafetyStatusPreference mSafetyStatusPreference;
private final CollapsableGroupCardHelper mCollapsableGroupCardHelper =
@@ -116,6 +118,8 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
mEntriesGroup = null;
getPreferenceScreen().removePreference(mStaticEntriesGroup);
mStaticEntriesGroup = null;
+ Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
+ getPreferenceScreen().removePreference(spacerPreference);
}
getSafetyCenterViewModel().getStatusUiLiveData().observe(this, this::updateStatus);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
index 8625e1959..5e45d2b3c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
@@ -105,7 +105,9 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
Log.w(TAG, "$sourceGroupId doesn't have any matching footer")
subpageFooter.setVisible(false)
}
-
+ // footer is ordered last by default
+ // in order to keep a spacer after the footer, footer needs to be the second from last
+ subpageFooter.setOrder(Int.MAX_VALUE - 2)
subpageFooter.setSummary(footerText)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
new file mode 100644
index 000000000..bb09783be
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
@@ -0,0 +1,129 @@
+/*
+ * 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
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.preference.PreferenceViewHolder
+import com.android.permissioncontroller.R
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity
+import com.android.settingslib.widget.FooterPreference
+import kotlin.math.max
+
+/**
+ * A preference that adds an empty space to the bottom of a Safety Center subpage.
+ *
+ * Due to the logic of [CollapsingToolbarBaseActivity], its content won't be scrollable if it fits
+ * the single page. This logic conflicts with the UX of collapsible and expandable items of Safety
+ * Center, and with some other use cases (i.e. opening the page from Search might scroll to bottom
+ * while the scroll is disabled). In such cases user won't be able to expand the collapsed toolbar
+ * by scrolling the screen content.
+ *
+ * [SpacerPreference] makes the page to be slightly bigger than the screen size to unlock the scroll
+ * regardless of the content length and to mitigate this UX problem.
+ *
+ * If a [FooterPreference] is added to the same [PreferenceScreen], its order should be decreased to
+ * keep it with the last visible content above the [SpacerPreference].
+ */
+internal class SpacerPreference(context: Context, attrs: AttributeSet) :
+ Preference(context, attrs) {
+
+ init {
+ setLayoutResource(R.layout.preference_spacer)
+ isVisible = SafetyCenterUiFlags.getShowSubpages()
+ // spacer should be the last item on screen
+ setOrder(Int.MAX_VALUE - 1)
+ }
+
+ private var maxKnownToolbarHeight = 0
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ val spacer = holder.itemView
+
+ // we should ensure we won't add multiple listeners to the same view,
+ // and Preferences API does not allow to do cleanups when onViewRecycled,
+ // so we are keeping a track of the added listener attaching it as a tag to the View
+ val listener: View.OnLayoutChangeListener = spacer.tag as? View.OnLayoutChangeListener
+ ?: object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ adjustHeight(spacer)
+ }}.also { spacer.tag = it }
+
+ spacer.removeOnLayoutChangeListener(listener)
+ spacer.addOnLayoutChangeListener(listener)
+ }
+
+ private fun adjustHeight(spacer: View) {
+ val root = spacer.rootView as? ViewGroup
+ if (root == null) {
+ return
+ }
+
+ val contentParent = root.findViewById<ViewGroup>(R.id.content_parent)
+ if (contentParent == null) {
+ return
+ }
+ // when opening the Subpage from Search the layout pass may be triggered
+ // differently due to the auto-scroll to highlight a specific item,
+ // and in this case we need to wait the content parent to be measured
+ if (contentParent.height == 0) {
+ val globalLayoutObserver = object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ adjustHeight(spacer)
+ }
+ }
+ contentParent.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutObserver)
+ return
+ }
+
+ val collapsingToolbar = root.findViewById<View>(R.id.collapsing_toolbar)
+ maxKnownToolbarHeight = max(maxKnownToolbarHeight, collapsingToolbar.height)
+
+ val contentHeight = spacer.top + maxKnownToolbarHeight
+ val desiredSpacerHeight = if (contentHeight > contentParent.height) {
+ // making it 0 height will remove if from recyclerview
+ 1
+ } else {
+ // to unlock the scrolling we need spacer to go slightly beyond the screen
+ contentParent.height - contentHeight + 1
+ }
+
+ val layoutParams = spacer.layoutParams
+ if (layoutParams.height != desiredSpacerHeight) {
+ layoutParams.height = desiredSpacerHeight
+ spacer.layoutParams = layoutParams
+ // need to let RecyclerView to update scroll position
+ spacer.post(::notifyChanged)
+ }
+ }
+}