• 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 android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
20 import android.os.Bundle
21 import android.safetycenter.SafetyCenterEntryGroup
22 import android.util.Log
23 import androidx.annotation.RequiresApi
24 import androidx.preference.PreferenceGroup
25 import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
26 import com.android.permissioncontroller.R
27 import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreference.Companion.closeSubpage
28 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
29 import com.android.safetycenter.resources.SafetyCenterResourcesContext
30 import com.android.settingslib.widget.FooterPreference
31 
32 /** A fragment that represents a generic subpage in Safety Center. */
33 @RequiresApi(UPSIDE_DOWN_CAKE)
34 class SafetyCenterSubpageFragment : SafetyCenterFragment() {
35 
36     private lateinit var sourceGroupId: String
37     private lateinit var subpageBrandChip: SafetyBrandChipPreference
38     private lateinit var subpageIllustration: SafetyIllustrationPreference
39     private lateinit var subpageIssueGroup: PreferenceGroup
40     private lateinit var subpageEntryGroup: PreferenceGroup
41     private lateinit var subpageFooter: FooterPreference
42 
onCreatePreferencesnull43     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
44         super.onCreatePreferences(savedInstanceState, rootKey)
45         setPreferencesFromResource(R.xml.safety_center_subpage, rootKey)
46         sourceGroupId = requireArguments().getString(SOURCE_GROUP_ID_KEY)!!
47 
48         subpageBrandChip = getPreferenceScreen().findPreference(BRAND_CHIP_KEY)!!
49         subpageIllustration = getPreferenceScreen().findPreference(ILLUSTRATION_KEY)!!
50         subpageIssueGroup = getPreferenceScreen().findPreference(ISSUE_GROUP_KEY)!!
51         subpageEntryGroup = getPreferenceScreen().findPreference(ENTRY_GROUP_KEY)!!
52         subpageFooter = getPreferenceScreen().findPreference(FOOTER_KEY)!!
53 
54         subpageBrandChip.setupListener(requireActivity(), safetyCenterSessionId)
55         setupIllustration()
56         setupFooter()
57 
58         prerenderCurrentSafetyCenterData()
59     }
60 
configureInteractionLoggernull61     override fun configureInteractionLogger() {
62         val logger = safetyCenterViewModel.interactionLogger
63         logger.sessionId = safetyCenterSessionId
64         logger.navigationSource = NavigationSource.fromIntent(requireActivity().getIntent())
65         logger.viewType = ViewType.SUBPAGE
66         logger.groupId = sourceGroupId
67     }
68 
onResumenull69     override fun onResume() {
70         super.onResume()
71         safetyCenterViewModel.pageOpen(sourceGroupId)
72     }
73 
renderSafetyCenterDatanull74     override fun renderSafetyCenterData(uiData: SafetyCenterUiData?) {
75         Log.d(TAG, "renderSafetyCenterEntryGroup called with $uiData")
76         val entryGroup = uiData?.getMatchingGroup(sourceGroupId)
77         if (entryGroup == null) {
78             Log.w(TAG, "$sourceGroupId doesn't match any of the existing SafetySourcesGroup IDs")
79             closeSubpage(requireActivity(), requireContext(), safetyCenterSessionId)
80             return
81         }
82 
83         requireActivity().setTitle(entryGroup.title)
84         updateSafetyCenterIssues(uiData)
85         updateSafetyCenterEntries(entryGroup)
86     }
87 
setupIllustrationnull88     private fun setupIllustration() {
89         val resName = "illustration_${SnakeCaseConverter.fromCamelCase(sourceGroupId)}"
90         val context = requireContext()
91         val drawable =
92             SafetyCenterResourcesContext(context).getDrawableByName(resName, context.theme)
93         if (drawable == null) {
94             Log.w(TAG, "$sourceGroupId doesn't have any matching illustration")
95             subpageIllustration.setVisible(false)
96         }
97 
98         subpageIllustration.illustrationDrawable = drawable
99     }
100 
setupFooternull101     private fun setupFooter() {
102         val resName = "${SnakeCaseConverter.fromCamelCase(sourceGroupId)}_footer"
103         val footerText = SafetyCenterResourcesContext(requireContext()).getStringByName(resName)
104         if (footerText.isEmpty()) {
105             Log.w(TAG, "$sourceGroupId doesn't have any matching footer")
106             subpageFooter.setVisible(false)
107         }
108         // footer is ordered last by default
109         // in order to keep a spacer after the footer, footer needs to be the second from last
110         subpageFooter.setOrder(Int.MAX_VALUE - 2)
111         subpageFooter.setSummary(footerText)
112     }
113 
updateSafetyCenterIssuesnull114     private fun updateSafetyCenterIssues(uiData: SafetyCenterUiData?) {
115         subpageIssueGroup.removeAll()
116         val subpageIssues = uiData?.getMatchingIssues(sourceGroupId)
117         val subpageDismissedIssues = uiData?.getMatchingDismissedIssues(sourceGroupId)
118 
119         subpageIllustration.isVisible =
120             subpageIssues.isNullOrEmpty() && subpageIllustration.illustrationDrawable != null
121 
122         if (subpageIssues.isNullOrEmpty() && subpageDismissedIssues.isNullOrEmpty()) {
123             Log.w(TAG, "$sourceGroupId doesn't have any matching SafetyCenterIssues")
124             return
125         }
126 
127         collapsableIssuesCardHelper.addIssues(
128             requireContext(),
129             safetyCenterViewModel,
130             getChildFragmentManager(),
131             subpageIssueGroup,
132             subpageIssues,
133             subpageDismissedIssues,
134             uiData.resolvedIssues,
135             requireActivity().getTaskId()
136         )
137     }
138 
updateSafetyCenterEntriesnull139     private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
140         Log.d(TAG, "updateSafetyCenterEntries called with $entryGroup")
141         subpageEntryGroup.removeAll()
142         for (entry in entryGroup.entries) {
143             subpageEntryGroup.addPreference(
144                 SafetySubpageEntryPreference(
145                     requireContext(),
146                     PendingIntentSender.getTaskIdForEntry(
147                         entry.id,
148                         sameTaskSourceIds,
149                         requireActivity()
150                     ),
151                     entry,
152                     safetyCenterViewModel
153                 )
154             )
155         }
156     }
157 
158     companion object {
159         private val TAG: String = SafetyCenterSubpageFragment::class.java.simpleName
160         private const val BRAND_CHIP_KEY: String = "subpage_brand_chip"
161         private const val ILLUSTRATION_KEY: String = "subpage_illustration"
162         private const val ISSUE_GROUP_KEY: String = "subpage_issue_group"
163         private const val ENTRY_GROUP_KEY: String = "subpage_entry_group"
164         private const val FOOTER_KEY: String = "subpage_footer"
165         private const val SOURCE_GROUP_ID_KEY: String = "source_group_id"
166 
167         /** Creates an instance of SafetyCenterSubpageFragment with the arguments set */
168         @JvmStatic
newInstancenull169         fun newInstance(sessionId: Long, groupId: String): SafetyCenterSubpageFragment {
170             val args = Bundle()
171             args.putLong(EXTRA_SESSION_ID, sessionId)
172             args.putString(SOURCE_GROUP_ID_KEY, groupId)
173 
174             val subpageFragment = SafetyCenterSubpageFragment()
175             subpageFragment.setArguments(args)
176             return subpageFragment
177         }
178     }
179 }
180