• 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 
21 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
22 import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT;
23 
24 import static java.util.Collections.emptyList;
25 import static java.util.Objects.requireNonNull;
26 
27 import android.content.Context;
28 import android.content.Intent;
29 import android.graphics.Color;
30 import android.graphics.drawable.ColorDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.os.Bundle;
33 import android.safetycenter.SafetyCenterData;
34 import android.safetycenter.SafetyCenterEntry;
35 import android.safetycenter.SafetyCenterEntryGroup;
36 import android.safetycenter.SafetyCenterEntryOrGroup;
37 import android.safetycenter.SafetyCenterIssue;
38 import android.safetycenter.SafetyCenterStaticEntry;
39 import android.safetycenter.SafetyCenterStaticEntryGroup;
40 import android.util.Log;
41 
42 import androidx.annotation.Nullable;
43 import androidx.annotation.RequiresApi;
44 import androidx.preference.Preference;
45 import androidx.preference.PreferenceCategory;
46 import androidx.preference.PreferenceGroup;
47 
48 import com.android.permissioncontroller.R;
49 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData;
50 import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
51 import com.android.safetycenter.internaldata.SafetyCenterBundles;
52 import com.android.safetycenter.resources.SafetyCenterResourcesContext;
53 
54 import kotlin.Unit;
55 
56 import java.util.List;
57 import java.util.Map;
58 
59 /** Dashboard fragment for the Safety Center. */
60 @RequiresApi(TIRAMISU)
61 public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
62 
63     private static final String TAG = SafetyCenterDashboardFragment.class.getSimpleName();
64 
65     private static final String SAFETY_STATUS_KEY = "safety_status";
66     private static final String ISSUES_GROUP_KEY = "issues_group";
67     private static final String ENTRIES_GROUP_KEY = "entries_group";
68     private static final String STATIC_ENTRIES_GROUP_KEY = "static_entries_group";
69     private static final String SPACER_KEY = "spacer";
70 
71     private SafetyStatusPreference mSafetyStatusPreference;
72     private final CollapsableGroupCardHelper mCollapsableGroupCardHelper =
73             new CollapsableGroupCardHelper();
74     private PreferenceGroup mIssuesGroup;
75     private PreferenceGroup mEntriesGroup;
76     private PreferenceGroup mStaticEntriesGroup;
77     private boolean mIsQuickSettingsFragment;
78 
SafetyCenterDashboardFragment()79     public SafetyCenterDashboardFragment() {}
80 
81     /**
82      * Create instance of SafetyCenterDashboardFragment with the arguments set
83      *
84      * @param isQuickSettingsFragment Denoting if it is the quick settings fragment
85      * @return SafetyCenterDashboardFragment with the arguments set
86      */
newInstance( long sessionId, boolean isQuickSettingsFragment)87     public static SafetyCenterDashboardFragment newInstance(
88             long sessionId, boolean isQuickSettingsFragment) {
89         Bundle args = new Bundle();
90         args.putLong(EXTRA_SESSION_ID, sessionId);
91         args.putBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, isQuickSettingsFragment);
92 
93         SafetyCenterDashboardFragment frag = new SafetyCenterDashboardFragment();
94         frag.setArguments(args);
95         return frag;
96     }
97 
98     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)99     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
100         super.onCreatePreferences(savedInstanceState, rootKey);
101         setPreferencesFromResource(R.xml.safety_center_dashboard, rootKey);
102 
103         if (getArguments() != null) {
104             mIsQuickSettingsFragment =
105                     getArguments().getBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, false);
106         }
107         mCollapsableGroupCardHelper.restoreState(savedInstanceState);
108 
109         mSafetyStatusPreference =
110                 requireNonNull(getPreferenceScreen().findPreference(SAFETY_STATUS_KEY));
111         mSafetyStatusPreference.setViewModel(getSafetyCenterViewModel());
112 
113         mIssuesGroup = getPreferenceScreen().findPreference(ISSUES_GROUP_KEY);
114         mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY);
115         mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY);
116         if (mIsQuickSettingsFragment) {
117             getPreferenceScreen().removePreference(mEntriesGroup);
118             mEntriesGroup = null;
119             getPreferenceScreen().removePreference(mStaticEntriesGroup);
120             mStaticEntriesGroup = null;
121             Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
122             getPreferenceScreen().removePreference(spacerPreference);
123         }
124         getSafetyCenterViewModel().getStatusUiLiveData().observe(this, this::updateStatus);
125 
126         prerenderCurrentSafetyCenterData();
127     }
128 
129     // Set the default divider line between preferences to be transparent
130     @Override
setDivider(Drawable divider)131     public void setDivider(Drawable divider) {
132         super.setDivider(new ColorDrawable(Color.TRANSPARENT));
133     }
134 
135     @Override
setDividerHeight(int height)136     public void setDividerHeight(int height) {
137         super.setDividerHeight(0);
138     }
139 
140     @Override
onResume()141     public void onResume() {
142         super.onResume();
143         getSafetyCenterViewModel().pageOpen();
144     }
145 
146     @Override
configureInteractionLogger()147     public void configureInteractionLogger() {
148         InteractionLogger logger = getSafetyCenterViewModel().getInteractionLogger();
149         logger.setSessionId(getSafetyCenterSessionId());
150         logger.setViewType(mIsQuickSettingsFragment ? ViewType.QUICK_SETTINGS : ViewType.FULL);
151 
152         Intent intent = requireActivity().getIntent();
153         logger.setNavigationSource(NavigationSource.fromIntent(intent));
154         logger.setNavigationSensor(Sensor.fromIntent(intent));
155     }
156 
157     @Override
onSaveInstanceState(Bundle outState)158     public void onSaveInstanceState(Bundle outState) {
159         super.onSaveInstanceState(outState);
160         mCollapsableGroupCardHelper.saveState(outState);
161     }
162 
updateStatus(StatusUiData statusUiData)163     private void updateStatus(StatusUiData statusUiData) {
164         if (mIsQuickSettingsFragment) {
165             SafetyCenterResourcesContext safetyCenterResourcesContext =
166                     new SafetyCenterResourcesContext(requireContext());
167             boolean hasPendingActions =
168                     safetyCenterResourcesContext
169                             .getStringByName("overall_severity_level_ok_review_summary")
170                             .equals(statusUiData.getOriginalSummary().toString());
171 
172             statusUiData = statusUiData.copyForPendingActions(hasPendingActions);
173         }
174 
175         mSafetyStatusPreference.setData(statusUiData);
176     }
177 
178     @Override
renderSafetyCenterData(@ullable SafetyCenterUiData uiData)179     public void renderSafetyCenterData(@Nullable SafetyCenterUiData uiData) {
180         if (uiData == null) return;
181         SafetyCenterData data = uiData.getSafetyCenterData();
182 
183         Log.i(TAG, String.format("renderSafetyCenterData called with: %s", data));
184 
185         Context context = getContext();
186         if (context == null) {
187             return;
188         }
189 
190         // TODO(b/208212820): Only update entries that have changed since last
191         // update, rather than deleting and re-adding all.
192         updateIssues(context, data.getIssues(), uiData.getResolvedIssues());
193 
194         if (!mIsQuickSettingsFragment) {
195             updateSafetyEntries(context, data.getEntriesOrGroups());
196             updateStaticSafetyEntries(context, data);
197         }
198     }
199 
updateIssues( Context context, List<SafetyCenterIssue> issues, Map<String, String> resolvedIssues)200     private void updateIssues(
201             Context context, List<SafetyCenterIssue> issues, Map<String, String> resolvedIssues) {
202         mIssuesGroup.removeAll();
203         getCollapsableIssuesCardHelper()
204                 .addIssues(
205                         context,
206                         getSafetyCenterViewModel(),
207                         getChildFragmentManager(),
208                         mIssuesGroup,
209                         issues,
210                         emptyList(),
211                         resolvedIssues,
212                         getActivity().getTaskId());
213     }
214 
215     // TODO(b/208212820): Add groups and move to separate controller
updateSafetyEntries( Context context, List<SafetyCenterEntryOrGroup> entriesOrGroups)216     private void updateSafetyEntries(
217             Context context, List<SafetyCenterEntryOrGroup> entriesOrGroups) {
218         mEntriesGroup.removeAll();
219 
220         for (int i = 0, size = entriesOrGroups.size(); i < size; i++) {
221             SafetyCenterEntryOrGroup entryOrGroup = entriesOrGroups.get(i);
222             SafetyCenterEntry entry = entryOrGroup.getEntry();
223             SafetyCenterEntryGroup group = entryOrGroup.getEntryGroup();
224 
225             boolean isFirstElement = i == 0;
226             boolean isLastElement = i == size - 1;
227 
228             if (SafetyCenterUiFlags.getShowSubpages() && group != null) {
229                 mEntriesGroup.addPreference(
230                         new SafetyHomepageEntryPreference(
231                                 context, group, getSafetyCenterSessionId()));
232             } else if (entry != null) {
233                 addTopLevelEntry(context, entry, isFirstElement, isLastElement);
234             } else if (group != null) {
235                 addGroupEntries(context, group, isFirstElement, isLastElement);
236             }
237         }
238     }
239 
addTopLevelEntry( Context context, SafetyCenterEntry entry, boolean isFirstElement, boolean isLastElement)240     private void addTopLevelEntry(
241             Context context,
242             SafetyCenterEntry entry,
243             boolean isFirstElement,
244             boolean isLastElement) {
245         mEntriesGroup.addPreference(
246                 new SafetyEntryPreference(
247                         context,
248                         PendingIntentSender.getTaskIdForEntry(
249                                 entry.getId(), getSameTaskSourceIds(), requireActivity()),
250                         entry,
251                         PositionInCardList.calculate(isFirstElement, isLastElement),
252                         getSafetyCenterViewModel()));
253     }
254 
addGroupEntries( Context context, SafetyCenterEntryGroup group, boolean isFirstCard, boolean isLastCard)255     private void addGroupEntries(
256             Context context,
257             SafetyCenterEntryGroup group,
258             boolean isFirstCard,
259             boolean isLastCard) {
260         mEntriesGroup.addPreference(
261                 new SafetyGroupPreference(
262                         context,
263                         group,
264                         mCollapsableGroupCardHelper::isGroupExpanded,
265                         isFirstCard,
266                         isLastCard,
267                         (entryId) ->
268                                 PendingIntentSender.getTaskIdForEntry(
269                                         entryId, getSameTaskSourceIds(), requireActivity()),
270                         getSafetyCenterViewModel(),
271                         (groupId) -> {
272                             mCollapsableGroupCardHelper.onGroupExpanded(groupId);
273                             return Unit.INSTANCE;
274                         },
275                         (groupId) -> {
276                             mCollapsableGroupCardHelper.onGroupCollapsed(groupId);
277                             return Unit.INSTANCE;
278                         }));
279     }
280 
updateStaticSafetyEntries(Context context, SafetyCenterData data)281     private void updateStaticSafetyEntries(Context context, SafetyCenterData data) {
282         mStaticEntriesGroup.removeAll();
283 
284         for (SafetyCenterStaticEntryGroup group : data.getStaticEntryGroups()) {
285             PreferenceCategory category = new ComparablePreferenceCategory(context);
286             category.setTitle(group.getTitle());
287             mStaticEntriesGroup.addPreference(category);
288 
289             for (SafetyCenterStaticEntry entry : group.getStaticEntries()) {
290                 category.addPreference(
291                         new StaticSafetyEntryPreference(
292                                 context,
293                                 requireActivity().getTaskId(),
294                                 entry,
295                                 SafetyCenterBundles.getStaticEntryId(data, entry),
296                                 getSafetyCenterViewModel()));
297             }
298         }
299     }
300 }
301