summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml191
-rw-r--r--PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml690
-rw-r--r--PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml690
-rw-r--r--PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml276
-rw-r--r--PermissionController/res/drawable-v33/status_scanning_anim_info.xml63
-rw-r--r--PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml61
-rw-r--r--PermissionController/res/drawable-v33/status_scanning_anim_warn.xml61
-rw-r--r--PermissionController/res/layout-v31/expand_button_with_large_title.xml4
-rw-r--r--PermissionController/res/layout-v33/preference_safety_status.xml70
-rw-r--r--PermissionController/res/values-v33/strings.xml5
-rw-r--r--PermissionController/res/values-v33/styles.xml124
-rw-r--r--PermissionController/res/values/strings.xml8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableIssuesCardHelper.kt24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java55
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java133
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt107
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt19
-rw-r--r--SafetyCenter/Resources/shared_res/values/strings.xml8
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java181
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataTracker.java138
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterFlags.java24
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterService.java112
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt241
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagedDeviceTest.kt5
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt341
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt8
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsHelper.kt1
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt23
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt228
32 files changed, 3186 insertions, 771 deletions
diff --git a/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml
new file mode 100644
index 000000000..e16d2f4e9
--- /dev/null
+++ b/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml
@@ -0,0 +1,191 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
+ android:viewportWidth="560">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_3_G" android:translateX="280" android:translateY="280"
+ android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="280" android:translateY="280"
+ android:scaleX="0" android:scaleY="0">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="281.25"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/colorScStatusInfo"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-14 -12.37 C-14,-12.37 0,-17.5 0,-17.5 C0,-17.5 14,-12.37 14,-12.37 C14,-12.37 14,-1.63 14,-1.63 C14,2.8 12.6,6.77 10.03,10.5 C7.47,14.23 3.97,16.57 0,17.5 C-3.97,16.57 -7.47,14.23 -10.03,10.5 C-12.6,6.77 -14,2.8 -14,-1.63 C-14,-1.63 -14,-12.37 -14,-12.37c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_0_G" android:translateX="0.125"
+ android:translateY="-0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_3_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="783"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml
new file mode 100644
index 000000000..6a2c9d9ec
--- /dev/null
+++ b/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml
@@ -0,0 +1,690 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
+ android:viewportWidth="560">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_5_G" android:translateX="280" android:translateY="280"
+ android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundRecommend"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_4_G" android:translateX="280" android:translateY="280"
+ android:scaleX="0" android:scaleY="0">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="281.25"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="?attr/colorScStatusRecommend"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-14 -12.37 C-14,-12.37 0,-17.5 0,-17.5 C0,-17.5 14,-12.37 14,-12.37 C14,-12.37 14,-1.63 14,-1.63 C14,2.8 12.6,6.77 10.03,10.5 C7.47,14.23 3.97,16.57 0,17.5 C-3.97,16.57 -7.47,14.23 -10.03,10.5 C-12.6,6.77 -14,2.8 -14,-1.63 C-14,-1.63 -14,-12.37 -14,-12.37c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="281.25"
+ android:translateY="275" android:rotation="135" android:scaleX="10"
+ android:scaleY="0">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "/>
+ </group>
+ <group android:name="_R_G_L_1_G_N_2_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_1_G_N_2_T_1" android:translateX="0"
+ android:translateY="-4" android:rotation="0">
+ <group android:name="_R_G_L_1_G_N_2_T_0" android:translateY="4">
+ <group android:name="_R_G_L_1_G_T_1" android:translateX="0"
+ android:translateY="5.333">
+ <group android:name="_R_G_L_1_G" android:translateY="-5.333">
+ <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"
+ android:pivotY="1.871" android:scaleX="1"
+ android:scaleY="1">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="?attr/colorScShieldAccent" android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 7.67 C1.29,7.67 2.33,6.62 2.33,5.33 C2.33,4.04 1.29,3 0,3 C-1.29,3 -2.33,4.04 -2.33,5.33 C-2.33,6.62 -1.29,7.67 0,7.67c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+ android:translateY="-4" android:rotation="0">
+ <group android:name="_R_G_L_0_G" android:translateY="4">
+ <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:pivotY="-8.616"
+ android:scaleX="1" android:scaleY="1">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M2.33 -8.67 C2.33,-8.67 -2.33,-8.67 -2.33,-8.67 C-2.33,-8.67 -2.33,0.67 -2.33,0.67 C-2.33,0.67 2.33,0.67 2.33,0.67 C2.33,0.67 2.33,-8.67 2.33,-8.67c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_5_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_4_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillColor" android:duration="150"
+ android:startOffset="0" android:valueFrom="?attr/colorScStatusRecommend"
+ android:valueTo="?attr/colorScStatusRecommend" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="167"
+ android:startOffset="150" android:valueFrom="?attr/colorScStatusRecommend"
+ android:valueTo="?attr/colorScStatusInfo" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="450"
+ android:startOffset="317" android:valueFrom="?attr/colorScStatusInfo"
+ android:valueTo="?attr/colorScStatusInfo" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="300"
+ android:startOffset="0"
+ android:valueFrom="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueTo="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="300"
+ android:valueFrom="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueTo="M-4.21 3.84 C-4.21,3.84 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -4.21,3.84 -4.21,3.84c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="117"
+ android:startOffset="333"
+ android:valueFrom="M-4.21 3.84 C-4.21,3.84 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -4.21,3.84 -4.21,3.84c "
+ android:valueTo="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.44,0.44 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="317"
+ android:startOffset="450"
+ android:valueFrom="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueTo="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="135"
+ android:valueTo="135" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="135"
+ android:valueTo="-49.449" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.319,0 0,0.887 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="350"
+ android:startOffset="283" android:valueFrom="-49.449"
+ android:valueTo="0" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="633" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="300" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="100"
+ android:startOffset="133" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.8,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="100"
+ android:startOffset="133" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.8,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="233" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="233" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="400"
+ android:startOffset="367" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="400"
+ android:startOffset="367" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,5.333C 0,5.333 0,5.333 0,5.333">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.2,0.2 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="200"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,5.333C 0,3.8870000000000005 0,-1.898 0,-3.344">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="433"
+ android:startOffset="333" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-3.344C 0,-3.344 0,-3.344 0,-3.344">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,0 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0,-4 0,-4 0,-4">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.3,0.3 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="150"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0.284,-4.155 1.417,-4.773 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="483"
+ android:startOffset="283" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 1.701,-4.928C 1.701,-4.928 1.701,-4.928 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.8,0.8 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="0"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="483"
+ android:startOffset="283" android:valueFrom="172"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="283"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="283" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="133" android:valueFrom="1"
+ android:valueTo="0.79" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.225,0 0.573,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="133" android:valueFrom="1"
+ android:valueTo="0.9400000000000001" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.225,0 0.573,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="483"
+ android:startOffset="283" android:valueFrom="0.79"
+ android:valueTo="0.79" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.299,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="483"
+ android:startOffset="283" android:valueFrom="0.9400000000000001"
+ android:valueTo="0.9400000000000001" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.299,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0,-4 0,-4 0,-4">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.3,0.3 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="150"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0.284,-4.155 1.417,-4.773 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="483"
+ android:startOffset="283" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 1.701,-4.928C 1.701,-4.928 1.701,-4.928 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.8,0.8 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="0"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="483"
+ android:startOffset="283" android:valueFrom="172"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="300" android:valueFrom="10" android:valueTo="0"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="783"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml
new file mode 100644
index 000000000..0fc02b3fa
--- /dev/null
+++ b/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml
@@ -0,0 +1,690 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
+ android:viewportWidth="560">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_5_G" android:translateX="280" android:translateY="280"
+ android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundWarn"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_4_G" android:translateX="280" android:translateY="280"
+ android:scaleX="0" android:scaleY="0">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="281.25"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="?attr/colorScStatusWarn"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-14 -12.37 C-14,-12.37 0,-17.5 0,-17.5 C0,-17.5 14,-12.37 14,-12.37 C14,-12.37 14,-1.63 14,-1.63 C14,2.8 12.6,6.77 10.03,10.5 C7.47,14.23 3.97,16.57 0,17.5 C-3.97,16.57 -7.47,14.23 -10.03,10.5 C-12.6,6.77 -14,2.8 -14,-1.63 C-14,-1.63 -14,-12.37 -14,-12.37c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="281.25"
+ android:translateY="275" android:rotation="135" android:scaleX="10"
+ android:scaleY="0">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "/>
+ </group>
+ <group android:name="_R_G_L_1_G_N_2_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_1_G_N_2_T_1" android:translateX="0"
+ android:translateY="-4" android:rotation="0">
+ <group android:name="_R_G_L_1_G_N_2_T_0" android:translateY="4">
+ <group android:name="_R_G_L_1_G_T_1" android:translateX="0"
+ android:translateY="5.333">
+ <group android:name="_R_G_L_1_G" android:translateY="-5.333">
+ <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"
+ android:pivotY="1.871" android:scaleX="1"
+ android:scaleY="1">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="?attr/colorScShieldAccent" android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 7.67 C1.29,7.67 2.33,6.62 2.33,5.33 C2.33,4.04 1.29,3 0,3 C-1.29,3 -2.33,4.04 -2.33,5.33 C-2.33,6.62 -1.29,7.67 0,7.67c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+ android:translateY="-4" android:rotation="0">
+ <group android:name="_R_G_L_0_G" android:translateY="4">
+ <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:pivotY="-8.616"
+ android:scaleX="1" android:scaleY="1">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M2.33 -8.67 C2.33,-8.67 -2.33,-8.67 -2.33,-8.67 C-2.33,-8.67 -2.33,0.67 -2.33,0.67 C-2.33,0.67 2.33,0.67 2.33,0.67 C2.33,0.67 2.33,-8.67 2.33,-8.67c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_5_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_4_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillColor" android:duration="150"
+ android:startOffset="0" android:valueFrom="?attr/colorScStatusWarn"
+ android:valueTo="?attr/colorScStatusWarn" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="167"
+ android:startOffset="150" android:valueFrom="?attr/colorScStatusWarn"
+ android:valueTo="?attr/colorScStatusInfo" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="450"
+ android:startOffset="317" android:valueFrom="?attr/colorScStatusInfo"
+ android:valueTo="?attr/colorScStatusInfo" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="300"
+ android:startOffset="0"
+ android:valueFrom="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueTo="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="300"
+ android:valueFrom="M-2.21 1.86 C-2.21,1.86 0.25,4.33 0.25,4.33 C0.25,4.33 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -2.21,1.86 -2.21,1.86c "
+ android:valueTo="M-4.21 3.84 C-4.21,3.84 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -4.21,3.84 -4.21,3.84c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="117"
+ android:startOffset="333"
+ android:valueFrom="M-4.21 3.84 C-4.21,3.84 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -1.76,1.39 -1.76,1.39 C-1.76,1.39 -4.21,3.84 -4.21,3.84c "
+ android:valueTo="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.44,0.44 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="317"
+ android:startOffset="450"
+ android:valueFrom="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueTo="M-7.87 0.18 C-7.87,0.18 -1.75,6.3 -1.75,6.3 C-1.75,6.3 8.05,-3.5 8.05,-3.5 C8.05,-3.5 5.6,-5.95 5.6,-5.95 C5.6,-5.95 -1.75,1.4 -1.75,1.4 C-1.75,1.4 -5.42,-2.27 -5.42,-2.27 C-5.42,-2.27 -7.87,0.18 -7.87,0.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="135"
+ android:valueTo="135" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="135"
+ android:valueTo="-49.449" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.319,0 0,0.887 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="350"
+ android:startOffset="283" android:valueFrom="-49.449"
+ android:valueTo="0" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="633" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="300" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="100"
+ android:startOffset="133" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.8,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="100"
+ android:startOffset="133" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.8,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="233" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="233" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="400"
+ android:startOffset="367" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="400"
+ android:startOffset="367" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,5.333C 0,5.333 0,5.333 0,5.333">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.2,0.2 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="200"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,5.333C 0,3.8870000000000005 0,-1.898 0,-3.344">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="433"
+ android:startOffset="333" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-3.344C 0,-3.344 0,-3.344 0,-3.344">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,0 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0,-4 0,-4 0,-4">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.3,0.3 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="150"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0.284,-4.155 1.417,-4.773 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="483"
+ android:startOffset="283" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 1.701,-4.928C 1.701,-4.928 1.701,-4.928 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.8,0.8 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="0"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="483"
+ android:startOffset="283" android:valueFrom="172"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="283"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="283" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="133"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="133" android:valueFrom="1"
+ android:valueTo="0.79" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.225,0 0.573,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="133" android:valueFrom="1"
+ android:valueTo="0.9400000000000001" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.225,0 0.573,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="483"
+ android:startOffset="283" android:valueFrom="0.79"
+ android:valueTo="0.79" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.299,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="483"
+ android:startOffset="283" android:valueFrom="0.9400000000000001"
+ android:valueTo="0.9400000000000001" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.299,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="133"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0,-4 0,-4 0,-4">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.3,0.3 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="150"
+ android:startOffset="133" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-4C 0.284,-4.155 1.417,-4.773 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="translateXY" android:duration="483"
+ android:startOffset="283" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 1.701,-4.928C 1.701,-4.928 1.701,-4.928 1.701,-4.928">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.8,0.8 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="133"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="150"
+ android:startOffset="133" android:valueFrom="0"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="rotation" android:duration="483"
+ android:startOffset="283" android:valueFrom="172"
+ android:valueTo="172" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="300" android:valueFrom="10" android:valueTo="0"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="783"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml b/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml
new file mode 100644
index 000000000..16023a6e1
--- /dev/null
+++ b/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml
@@ -0,0 +1,276 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
+ android:viewportWidth="560">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_4_G" android:translateX="280" android:translateY="280"
+ android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundWarn"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="280" android:translateY="280"
+ android:scaleX="0" android:scaleY="0">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundRecommend"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="281.25"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScStatusWarn"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M-14 -12.37 C-14,-12.37 0,-17.5 0,-17.5 C0,-17.5 14,-12.37 14,-12.37 C14,-12.37 14,-1.63 14,-1.63 C14,2.8 12.6,6.77 10.03,10.5 C7.47,14.23 3.97,16.57 0,17.5 C-3.97,16.57 -7.47,14.23 -10.03,10.5 C-12.6,6.77 -14,2.8 -14,-1.63 C-14,-1.63 -14,-12.37 -14,-12.37c "/>
+ </group>
+ <group android:name="_R_G_L_1_G_N_2_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_1_G">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M0 7.67 C1.29,7.67 2.33,6.62 2.33,5.33 C2.33,4.04 1.29,3 0,3 C-1.29,3 -2.33,4.04 -2.33,5.33 C-2.33,6.62 -1.29,7.67 0,7.67c "/>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="280"
+ android:translateY="280" android:scaleX="10" android:scaleY="10">
+ <group android:name="_R_G_L_0_G">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="?attr/colorScShieldAccent"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M2.33 -8.67 C2.33,-8.67 -2.33,-8.67 -2.33,-8.67 C-2.33,-8.67 -2.33,0.67 -2.33,0.67 C-2.33,0.67 2.33,0.67 2.33,0.67 C2.33,0.67 2.33,-8.67 2.33,-8.67c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_4_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="517"
+ android:startOffset="250" android:valueFrom="0" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.542 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="fillColor" android:duration="150"
+ android:startOffset="0" android:valueFrom="?attr/colorScStatusWarn"
+ android:valueTo="?attr/colorScStatusWarn" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="167"
+ android:startOffset="150" android:valueFrom="?attr/colorScStatusWarn"
+ android:valueTo="?attr/colorScStatusRecommend" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="fillColor" android:duration="450"
+ android:startOffset="317" android:valueFrom="?attr/colorScStatusRecommend"
+ android:valueTo="?attr/colorScStatusRecommend" android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_2_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="150"
+ android:startOffset="0" android:valueFrom="10" android:valueTo="9"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="450"
+ android:startOffset="150" android:valueFrom="9" android:valueTo="10"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleX" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="167"
+ android:startOffset="600" android:valueFrom="10"
+ android:valueTo="10" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0,0 0.833,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="783"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_info.xml b/PermissionController/res/drawable-v33/status_scanning_anim_info.xml
index 24c222f51..dca4fe07a 100644
--- a/PermissionController/res/drawable-v33/status_scanning_anim_info.xml
+++ b/PermissionController/res/drawable-v33/status_scanning_anim_info.xml
@@ -4,14 +4,12 @@
<vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
android:viewportWidth="560">
<group android:name="_R_G">
- <group android:name="_R_G_L_2_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="1">
- <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ <group android:name="_R_G_L_2_G">
+ <path android:name="_R_G_L_2_G_S" android:fillColor="?attr/colorSurface"
+ android:pathData="M0,0 L560,0 L560,560 L0,560z"/>
</group>
<group android:name="_R_G_L_1_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="0">
+ android:scaleX="1" android:scaleY="1">
<path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundInfo"
android:fillAlpha="1" android:fillType="nonZero"
android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
@@ -26,56 +24,38 @@
<group android:name="time_group"/>
</vector>
</aapt:attr>
- <target android:name="_R_G_L_2_G">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="583"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="417"
+ android:startOffset="583" android:valueFrom="1" android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2017" android:valueFrom="1" android:valueTo="0"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="_R_G_L_1_G">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleX" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleY" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
@@ -84,22 +64,13 @@
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_1_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2167" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="translateX" android:duration="2183"
+ <objectAnimator android:propertyName="translateX" android:duration="1017"
android:startOffset="0" android:valueFrom="0" android:valueTo="1"
android:valueType="floatType"/>
</set>
</aapt:attr>
</target>
-</animated-vector>
+</animated-vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml b/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml
index f5e9fb4b6..ecaf86c0c 100644
--- a/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml
+++ b/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml
@@ -4,14 +4,12 @@
<vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
android:viewportWidth="560">
<group android:name="_R_G">
- <group android:name="_R_G_L_2_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="1">
- <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundRecommend"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ <group android:name="_R_G_L_2_G">
+ <path android:name="_R_G_L_2_G_S" android:fillColor="?attr/colorSurface"
+ android:pathData="M0,0 L560,0 L560,560 L0,560z"/>
</group>
<group android:name="_R_G_L_1_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="0">
+ android:scaleX="1" android:scaleY="1">
<path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundRecommend"
android:fillAlpha="1" android:fillType="nonZero"
android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
@@ -26,56 +24,38 @@
<group android:name="time_group"/>
</vector>
</aapt:attr>
- <target android:name="_R_G_L_2_G">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="583"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="417"
+ android:startOffset="583" android:valueFrom="1" android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2017" android:valueFrom="1" android:valueTo="0"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="_R_G_L_1_G">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleX" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleY" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
@@ -84,19 +64,10 @@
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_1_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2167" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="translateX" android:duration="2183"
+ <objectAnimator android:propertyName="translateX" android:duration="1017"
android:startOffset="0" android:valueFrom="0" android:valueTo="1"
android:valueType="floatType"/>
</set>
diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml b/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml
index 64ac381c2..e2d651c7a 100644
--- a/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml
+++ b/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml
@@ -4,14 +4,12 @@
<vector android:height="560dp" android:width="560dp" android:viewportHeight="560"
android:viewportWidth="560">
<group android:name="_R_G">
- <group android:name="_R_G_L_2_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="1">
- <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundWarn"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
+ <group android:name="_R_G_L_2_G">
+ <path android:name="_R_G_L_2_G_S" android:fillColor="?attr/colorSurface"
+ android:pathData="M0,0 L560,0 L560,560 L0,560z"/>
</group>
<group android:name="_R_G_L_1_G" android:translateX="280" android:translateY="280"
- android:scaleX="1" android:scaleY="0">
+ android:scaleX="1" android:scaleY="1">
<path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/colorScStatusBackgroundWarn"
android:fillAlpha="1" android:fillType="nonZero"
android:pathData=" M28 0 C28,15.46 15.46,28 0,28 C-15.46,28 -28,15.46 -28,0 C-28,-15.46 -15.46,-28 0,-28 C15.46,-28 28,-15.46 28,0c "/>
@@ -26,56 +24,38 @@
<group android:name="time_group"/>
</vector>
</aapt:attr>
- <target android:name="_R_G_L_2_G">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="583"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2000"
- android:startOffset="0" android:valueFrom="1" android:valueTo="10"
+ <objectAnimator android:propertyName="fillAlpha" android:duration="417"
+ android:startOffset="583" android:valueFrom="1" android:valueTo="0"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_2_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2017" android:valueFrom="1" android:valueTo="0"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="_R_G_L_1_G">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleX" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="2167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ <objectAnimator android:propertyName="scaleY" android:duration="1000"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="10"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
@@ -84,19 +64,10 @@
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_1_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleY" android:duration="0"
- android:startOffset="2167" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
- <objectAnimator android:propertyName="translateX" android:duration="2183"
+ <objectAnimator android:propertyName="translateX" android:duration="1017"
android:startOffset="0" android:valueFrom="0" android:valueTo="1"
android:valueType="floatType"/>
</set>
diff --git a/PermissionController/res/layout-v31/expand_button_with_large_title.xml b/PermissionController/res/layout-v31/expand_button_with_large_title.xml
index 66e162044..21eef1575 100644
--- a/PermissionController/res/layout-v31/expand_button_with_large_title.xml
+++ b/PermissionController/res/layout-v31/expand_button_with_large_title.xml
@@ -32,13 +32,13 @@
android:baselineAligned="false">
<ImageView
+ android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:maxWidth="24dp"
android:maxHeight="24dp"
- android:paddingEnd="24dp"
- android:src="@drawable/ic_expand_more" />
+ android:paddingEnd="24dp" />
<RelativeLayout
android:layout_width="0dp"
diff --git a/PermissionController/res/layout-v33/preference_safety_status.xml b/PermissionController/res/layout-v33/preference_safety_status.xml
index 4a3a6e8a6..30a6d59e0 100644
--- a/PermissionController/res/layout-v33/preference_safety_status.xml
+++ b/PermissionController/res/layout-v33/preference_safety_status.xml
@@ -17,97 +17,49 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
android:clickable="false"
- style="@style/SafetyCenter.StatusCard">
+ style="@style/SafetyCenterCard.Status">
<ImageView
android:id="@+id/status_image"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:scaleType="centerInside"
- android:gravity="center"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
android:importantForAccessibility="no"
- android:src="@drawable/safety_status_info" />
+ android:src="@drawable/safety_status_info"
+ style="@style/SafetyCenterStatusImage" />
<LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginTop="4dp"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="24dp"
android:id="@+id/status_title_and_summary"
- app:layout_constraintStart_toEndOf="@id/status_image"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent">
+ style="@style/SafetyCenterStatusTitleAndSummaryContainer">
<TextView
android:id="@+id/status_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
android:text="@string/summary_placeholder"
- android:textAppearance="@style/TextAppearance.SafetyStatusTitle"/>
+ style="@style/SafetyCenterStatusTitle" />
<TextView
android:id="@+id/status_summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/status_title"
android:text="@string/summary_placeholder"
- android:textAppearance="@style/TextAppearance.SafetyStatusSummary"/>
+ style="@style/SafetyCenterStatusSummary" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/review_settings_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/status_title_and_summary"
- app:layout_constraintStart_toStartOf="parent"
- android:layout_marginTop="24dp"
android:text="@string/safety_center_review_settings_button"
- app:backgroundTint="@color/safety_center_button_info"
android:visibility="gone"
- style="@style/SafetyCenter.MaterialActionButton" />
+ style="@style/SafetyCenterStatusButton.ReviewSettings" />
<com.google.android.material.button.MaterialButton
android:id="@+id/rescan_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/review_settings_button"
- app:layout_constraintStart_toStartOf="parent"
- android:layout_marginTop="24dp"
android:text="@string/safety_center_rescan_button"
- app:backgroundTint="@color/safety_center_button_info"
- style="@style/SafetyCenter.MaterialActionButton" />
+ style="@style/SafetyCenterStatusButton.Rescan" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pending_actions_rescan_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/review_settings_button"
- app:layout_constraintStart_toStartOf="parent"
- android:layout_marginTop="@dimen/safety_center_action_button_list_margin"
android:text="@string/safety_center_rescan_button"
- app:backgroundTint="@color/sc_surface_dark"
- app:strokeWidth="@dimen/mtrl_btn_stroke_size"
- app:strokeColor="@color/safety_center_button_info"
- android:textColor="?android:attr/textColorPrimary"
android:visibility="gone"
- style="@style/SafetyCenter.MaterialActionButton" />
+ style="@style/SafetyCenterStatusButton.PendingActionsRescan" />
<com.android.permissioncontroller.permission.ui.widget.SafetyProtectionSectionView
android:id="@+id/safety_protection_section_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/rescan_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- android:layout_marginTop="20dp"
- android:layout_marginBottom="0dp"
- android:layout_gravity="center"
- android:importantForAccessibility="noHideDescendants"/>
+ android:importantForAccessibility="noHideDescendants"
+ style="@style/SafetyCenterStatusSafetyProtectionView" />
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/PermissionController/res/values-v33/strings.xml b/PermissionController/res/values-v33/strings.xml
index b21280dbf..b8a2a1392 100644
--- a/PermissionController/res/values-v33/strings.xml
+++ b/PermissionController/res/values-v33/strings.xml
@@ -41,6 +41,11 @@
<!-- Content description containing the title, subtitle and summary of a safety center issue card preference [CHAR LIMIT=NONE]-->
<string name="safety_center_issue_card_content_description_with_subtitle">Alert. <xliff:g id="issue card title" example="Protect your account">%1$s</xliff:g>. <xliff:g id="issue card subtitle" example="testemail@google.com">%2$s</xliff:g>. <xliff:g id="issue card summary" example="Secure your account in the Security Checkup">%3$s</xliff:g></string>
+ <!-- TODO(b/237368717): Update issue resolution confirmation fallback string -->
+ <!-- Issue resolution confirmation text [CHAR LIMIT=NONE]-->
+ <string name="safety_center_issue_resolution_fallback">Complete</string>
+
+
<!--Summary for safety center status card QS in case of pending actions [CHAR LIMIT=NONE] -->
<string name="safety_center_qs_status_summary">Check settings that can add protection to your device</string>
<!-- Content description for the safety center quick settings page landing [CHAR LIMIT=NONE] -->
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml
index 6ab17e08d..44d9b8c53 100644
--- a/PermissionController/res/values-v33/styles.xml
+++ b/PermissionController/res/values-v33/styles.xml
@@ -14,7 +14,9 @@
~ limitations under the License.
-->
-<resources>
+<resources
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- START SAFETY CENTER QUICK SETTINGS PAGE -->
<style name="SafetyCenterLinkText">
@@ -34,8 +36,7 @@
<item name="android:background">@drawable/safety_center_button_background</item>
</style>
- <style name="SafetyCenterIndicatorCardView"
- xmlns:app="http://schemas.android.com/apk/res-auto" >
+ <style name="SafetyCenterIndicatorCardView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="app:cardCornerRadius">24dp</item>
@@ -102,21 +103,7 @@
<!-- START SAFETY CENTER SETTINGS PAGE -->
- <style name="TextAppearance.SafetyStatusTitle"
- parent="@android:style/TextAppearance.DeviceDefault.Headline">
- <item name="android:textSize">22sp</item>
- <item name="android:lineHeight">28sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.SafetyStatusSummary"
- parent="@android:style/TextAppearance.DeviceDefault.Small">
- <item name="android:textSize">14sp</item>
- <item name="android:lineHeight">20sp</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
-
- <style name="SafetyCenter.BaseCard"
+ <style name="SafetyCenterCard"
parent="android:Widget.DeviceDefault">
<item name="android:paddingStart">24dp</item>
<item name="android:paddingEnd">24dp</item>
@@ -176,11 +163,106 @@
<item name="android:textColor">?attr/textColorScSecondaryActionButton</item>
</style>
- <style name="SafetyCenter.StatusCard"
- parent="@style/SafetyCenter.BaseCard" />
+ <!-- START SAFETY STATUS CARD -->
+ <style name="SafetyCenterCard.Status">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="SafetyCenterStatusImage" parent="android:Widget.DeviceDefault">
+ <item name="android:layout_width">56dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="app:layout_constraintTop_toTopOf">parent</item>
+ <item name="app:layout_constraintStart_toStartOf">parent</item>
+ <item name="android:scaleType">centerInside</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="SafetyCenterStatusTitleAndSummaryContainer" parent="android:Widget.DeviceDefault">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:layout_marginTop">4dp</item>
+ <item name="android:layout_marginStart">16dp</item>
+ <item name="android:layout_marginEnd">24dp</item>
+ <item name="app:layout_constraintStart_toEndOf">@id/status_image</item>
+ <item name="app:layout_constraintEnd_toEndOf">parent</item>
+ <item name="app:layout_constraintTop_toTopOf">parent</item>
+ </style>
+
+ <style name="TextAppearance.SafetyStatusTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">22sp</item>
+ <item name="android:lineHeight">28sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="SafetyCenterStatusTitle" parent="android:Widget.DeviceDefault">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">@style/TextAppearance.SafetyStatusTitle</item>
+ </style>
+
+ <style name="TextAppearance.SafetyStatusSummary"
+ parent="@android:style/TextAppearance.DeviceDefault.Small">
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="SafetyCenterStatusSummary" parent="android:Widget.DeviceDefault">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="app:layout_constraintTop_toBottomOf">@id/status_title</item>
+ <item name="android:textAppearance">@style/TextAppearance.SafetyStatusSummary</item>
+ </style>
+
+ <style name="SafetyCenterStatusButton" parent="SafetyCenter.MaterialActionButton" />
+
+ <style name="SafetyCenterStatusButton.ReviewSettings">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="app:layout_constraintTop_toBottomOf">@id/status_title_and_summary</item>
+ <item name="app:layout_constraintStart_toStartOf">parent</item>
+ <item name="android:layout_marginTop">24dp</item>
+ <item name="app:backgroundTint">@color/safety_center_button_info</item>
+ </style>
+
+ <style name="SafetyCenterStatusButton.Rescan">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="app:layout_constraintTop_toBottomOf">@id/review_settings_button</item>
+ <item name="app:layout_constraintStart_toStartOf">parent</item>
+ <item name="android:layout_marginTop">24dp</item>
+ <item name="app:backgroundTint">@color/safety_center_button_info</item>
+ </style>
+
+ <style name="SafetyCenterStatusButton.PendingActionsRescan">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="app:layout_constraintTop_toBottomOf">@id/review_settings_button</item>
+ <item name="app:layout_constraintStart_toStartOf">parent</item>
+ <item name="android:layout_marginTop">@dimen/safety_center_action_button_list_margin</item>
+ <item name="app:backgroundTint">@color/sc_surface_dark</item>
+ <item name="app:strokeWidth">@dimen/mtrl_btn_stroke_size</item>
+ <item name="app:strokeColor">@color/safety_center_button_info</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="SafetyCenterStatusSafetyProtectionView">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="app:layout_constraintTop_toBottomOf">@id/rescan_button</item>
+ <item name="app:layout_constraintStart_toStartOf">parent</item>
+ <item name="app:layout_constraintEnd_toEndOf">parent</item>
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:layout_marginBottom">0dp</item>
+ <item name="android:layout_gravity">center</item>
+ </style>
+ <!-- END SAFETY STATUS CARD -->
<style name="SafetyCenter.IssueCard"
- parent="@style/SafetyCenter.BaseCard" />
+ parent="@style/SafetyCenterCard" />
<style name="SafetyCenter.IssueCard.TopRow"
parent="android:Widget.DeviceDefault">
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index de37bbaa1..93ee8f78f 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -873,14 +873,14 @@
<!-- The notification content for background location access reminder notification [CHAR LIMIT=none] -->
<string name="background_location_access_reminder_notification_content">This app can always access your location. Tap to change.</string>
- <!-- The notification title for background location access reminder notification [CHAR LIMIT=60] -->
+ <!-- The notification title for notification listener reminder notification [CHAR LIMIT=60] -->
<string name="notification_listener_reminder_notification_title">Review app with access to your notifications</string>
- <!-- The notification content for background location access reminder notification [CHAR LIMIT=none] -->
- <string name="notification_listener_reminder_notification_content"><xliff:g id="app_name" example="Gmail">%s</xliff:g> can access the content of all your notifications</string>
+ <!-- The notification content for notification listener reminder notification [CHAR LIMIT=none] -->
+ <string name="notification_listener_reminder_notification_content"><xliff:g id="app_name" example="Gmail">%s</xliff:g> can dismiss, act on, and access content inside your notifications</string>
<!-- The warning card (in safety center) content for notification listener access [CHAR LIMIT=none] -->
- <string name="notification_listener_warning_card_content">This app can access the content of all of your notifications. Some apps require this access to function as intended.</string>
+ <string name="notification_listener_warning_card_content">This app can dismiss, act on, and access content inside your notifications. Some apps require this access to function as intended.</string>
<!-- The label for remove access button on safety center warning card [CHAR LIMIT=60] -->
<string name="notification_listener_remove_access_button_label">Remove access</string>
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
index fa7f8120b..3c7a90b5b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
@@ -100,10 +100,12 @@ public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsVi
mSoftDenyButton.setOnClickListener(this);
mHardDenyButton.setOnClickListener(this);
- mRootView.addOnLayoutChangeListener((view, l, t, r, b, oldL, oldT, oldR, oldB) -> {
- mRootView.setUnrestrictedPreferKeepClearRects(
- Collections.singletonList(new Rect(0, 0, r - l, b - t)));
- });
+ if (SdkLevel.isAtLeastT()) {
+ mRootView.addOnLayoutChangeListener((view, l, t, r, b, oldL, oldT, oldR, oldB) -> {
+ mRootView.setUnrestrictedPreferKeepClearRects(
+ Collections.singletonList(new Rect(0, 0, r - l, b - t)));
+ });
+ }
return mRootView;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableIssuesCardHelper.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableIssuesCardHelper.kt
index 5b9cc6445..00fde7856 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableIssuesCardHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/CollapsableIssuesCardHelper.kt
@@ -21,11 +21,15 @@ import android.content.Intent
import android.content.Intent.ACTION_SAFETY_CENTER
import android.os.Build
import android.os.Bundle
+import android.safetycenter.SafetyCenterIssue
import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK
import androidx.annotation.RequiresApi
+import androidx.fragment.app.FragmentManager
import androidx.preference.PreferenceGroup
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.EXPAND_ISSUE_GROUP_QS_FRAGMENT_KEY
+import com.android.permissioncontroller.safetycenter.ui.model.ActionId
+import com.android.permissioncontroller.safetycenter.ui.model.IssueId
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
import com.android.safetycenter.internaldata.SafetyCenterIssueKey
import kotlin.math.max
@@ -78,17 +82,29 @@ class CollapsableIssuesCardHelper(val safetyCenterViewModel: SafetyCenterViewMod
* Add the [IssueCardPreference] managed by this helper to the specified [PreferenceGroup]
*
* @param context Current context
+ * @param safetyCenterViewModel {@link SafetyCenterViewModel} used when executing issue actions
+ * @param dialogFragmentManager fragment manager use for issue dismissal
* @param issuesPreferenceGroup Preference group to add preference to
- * @param issueCardPreferences {@link List} of {@link IssueCardPreference} to add to the
- * preference fragment
+ * @param issues {@link List} of {@link SafetyCenterIssue} to add to the preference fragment
+ * @param resolvedIssues {@link Map} of issue id to action ids of resolved issues
*/
fun addIssues(
context: Context,
+ safetyCenterViewModel: SafetyCenterViewModel,
+ dialogFragmentManager: FragmentManager,
issuesPreferenceGroup: PreferenceGroup,
- issueCardPreferences: List<IssueCardPreference>
+ issues: List<SafetyCenterIssue>,
+ resolvedIssues: Map<IssueId, ActionId>
) {
+ val issueCardPreferenceList: List<IssueCardPreference> =
+ issues.map { issue: SafetyCenterIssue ->
+ val resolvedActionId: ActionId? = resolvedIssues[issue.id]
+ IssueCardPreference(
+ context, safetyCenterViewModel, issue, resolvedActionId, dialogFragmentManager)
+ }
+
val (reorderedIssueCardPreferences, numberOfIssuesToShowWhenCollapsed) =
- maybeReorderFocusedSafetyCenterIssueInList(issueCardPreferences)
+ maybeReorderFocusedSafetyCenterIssueInList(issueCardPreferenceList)
val moreIssuesCardPreference =
createMoreIssuesCardPreference(
context,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
index 327ee8c69..935337ded 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
@@ -26,6 +26,8 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.safetycenter.SafetyCenterIssue;
import android.text.TextUtils;
import android.util.Log;
@@ -53,6 +55,8 @@ import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.google.android.material.button.MaterialButton;
+import java.util.Objects;
+
/** A preference that displays a card representing a {@link SafetyCenterIssue}. */
@RequiresApi(TIRAMISU)
public class IssueCardPreference extends Preference implements ComparablePreference {
@@ -63,11 +67,13 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
private final SafetyCenterIssue mIssue;
private final FragmentManager mDialogFragmentManager;
private final SafetyCenterIssueId mDecodedIssueId;
+ @Nullable private String mResolvedIssueActionId;
public IssueCardPreference(
Context context,
SafetyCenterViewModel safetyCenterViewModel,
SafetyCenterIssue issue,
+ @Nullable String resolvedIssueActionId,
FragmentManager dialogFragmentManager) {
super(context);
setLayoutResource(R.layout.preference_issue_card);
@@ -76,6 +82,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
mIssue = requireNonNull(issue);
mDialogFragmentManager = dialogFragmentManager;
mDecodedIssueId = SafetyCenterIds.issueIdFromString(mIssue.getId());
+ mResolvedIssueActionId = resolvedIssueActionId;
}
@Override
@@ -85,7 +92,8 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
configureDismissButton(holder.findViewById(R.id.issue_card_dismiss_btn));
((TextView) holder.findViewById(R.id.issue_card_title)).setText(mIssue.getTitle());
- ((TextView) holder.findViewById(R.id.issue_card_summary)).setText(mIssue.getSummary());
+ TextView issueCardSummary = (TextView) holder.findViewById(R.id.issue_card_summary);
+ issueCardSummary.setText(mIssue.getSummary());
CharSequence subtitle = mIssue.getSubtitle();
TextView subtitleTextView = (TextView) holder.findViewById(R.id.issue_card_subtitle);
@@ -94,20 +102,22 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
subtitleTextView.setVisibility(View.GONE);
contentDescription =
getContext()
- .getString(
- R.string.safety_center_issue_card_content_description,
- mIssue.getTitle(),
- mIssue.getSummary());
+ .getString(
+ R.string.safety_center_issue_card_content_description,
+ mIssue.getTitle(),
+ mIssue.getSummary());
} else {
subtitleTextView.setText(subtitle);
subtitleTextView.setVisibility(View.VISIBLE);
+ int contentDescriptionResId =
+ R.string.safety_center_issue_card_content_description_with_subtitle;
contentDescription =
getContext()
- .getString(
- R.string.safety_center_issue_card_content_description_with_subtitle,
- mIssue.getTitle(),
- mIssue.getSubtitle(),
- mIssue.getSummary());
+ .getString(
+ contentDescriptionResId,
+ mIssue.getTitle(),
+ mIssue.getSubtitle(),
+ mIssue.getSummary());
}
holder.itemView.setContentDescription(contentDescription);
holder.itemView.setClickable(false);
@@ -120,6 +130,26 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
buttonList.addView(
buildActionButton(action, holder.itemView.getContext(), isFirstButton));
isFirstButton = false;
+
+ // TODO(b/237368717): implement issue resolution confirmation UI
+ if (mResolvedIssueActionId != null && mResolvedIssueActionId.equals(action.getId())) {
+ CharSequence successMessage = action.getSuccessMessage();
+ if (TextUtils.isEmpty(successMessage)) {
+ issueCardSummary.setText(R.string.safety_center_issue_resolution_fallback);
+ } else {
+ issueCardSummary.setText(successMessage);
+ }
+
+ // TODO(b/237368717): remove once confirmation ui animation is implemented
+ new Handler(Looper.myLooper())
+ .postDelayed(
+ () -> {
+ mResolvedIssueActionId = null;
+ mSafetyCenterViewModel.markIssueResolvedUiCompleted(
+ mIssue.getId());
+ },
+ 5000);
+ }
}
mSafetyCenterViewModel
@@ -161,7 +191,10 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
@Override
public boolean hasSameContents(@NonNull Preference preference) {
return (preference instanceof IssueCardPreference)
- && mIssue.equals(((IssueCardPreference) preference).mIssue);
+ && mIssue.equals(((IssueCardPreference) preference).mIssue)
+ && Objects.equals(
+ mResolvedIssueActionId,
+ ((IssueCardPreference) preference).mResolvedIssueActionId);
}
private class DismissOnClickListener implements View.OnClickListener {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index 8ebc6db1c..5b5d82937 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -53,11 +53,12 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.permissioncontroller.Constants;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory;
+import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
import com.android.safetycenter.resources.SafetyCenterResourcesContext;
import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Map;
/** Dashboard fragment for the Safety Center. */
@RequiresApi(TIRAMISU)
@@ -172,7 +173,7 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa
mStaticEntriesGroup = null;
}
- mViewModel.getSafetyCenterLiveData().observe(this, this::renderSafetyCenterData);
+ mViewModel.getSafetyCenterUiLiveData().observe(this, this::renderSafetyCenterData);
mViewModel.getErrorLiveData().observe(this, this::displayErrorDetails);
getPreferenceManager()
@@ -225,8 +226,9 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa
return mViewModel;
}
- private void renderSafetyCenterData(@Nullable SafetyCenterData data) {
- if (data == null) return;
+ private void renderSafetyCenterData(@Nullable SafetyCenterUiData uiData) {
+ if (uiData == null) return;
+ SafetyCenterData data = uiData.getSafetyCenterData();
Log.i(TAG, String.format("renderSafetyCenterData called with: %s", data));
@@ -235,12 +237,11 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa
return;
}
- mSafetyStatusPreference.setSafetyStatus(data.getStatus());
- mSafetyStatusPreference.setHasIssues(!data.getIssues().isEmpty());
+ mSafetyStatusPreference.setSafetyData(data);
// TODO(b/208212820): Only update entries that have changed since last
// update, rather than deleting and re-adding all.
- updateIssues(context, data.getIssues());
+ updateIssues(context, data.getIssues(), uiData.getResolvedIssues());
if (!mIsQuickSettingsFragment) {
updateSafetyEntries(context, data.getEntriesOrGroups());
updateStaticSafetyEntries(context, data.getStaticEntryGroups());
@@ -277,19 +278,16 @@ public final class SafetyCenterDashboardFragment extends PreferenceFragmentCompa
mViewModel.clearError();
}
- private void updateIssues(Context context, List<SafetyCenterIssue> issues) {
+ private void updateIssues(
+ Context context, List<SafetyCenterIssue> issues, Map<String, String> resolvedIssues) {
mIssuesGroup.removeAll();
- List<IssueCardPreference> issueCardPreferenceList =
- issues.stream()
- .map(
- issue ->
- new IssueCardPreference(
- context,
- mViewModel,
- issue,
- getChildFragmentManager()))
- .collect(Collectors.toUnmodifiableList());
- mCollapsableIssuesCardHelper.addIssues(context, mIssuesGroup, issueCardPreferenceList);
+ mCollapsableIssuesCardHelper.addIssues(
+ context,
+ mViewModel,
+ getChildFragmentManager(),
+ mIssuesGroup,
+ issues,
+ resolvedIssues);
}
// TODO(b/208212820): Add groups and move to separate controller
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
index 6bb1fe9e4..93b6a36b0 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
+import android.safetycenter.SafetyCenterData;
import android.safetycenter.SafetyCenterStatus;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -56,13 +57,14 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
public SafetyStatusPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- mHasIssues = false;
setLayoutResource(R.layout.preference_safety_status);
}
- private boolean mRefreshRunning;
- private boolean mRefreshEnding;
- private int mCurrentSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN;
+ private boolean mIsScanAnimationRunning;
+ private boolean mIsIconChangeAnimationRunning;
+ private int mQueuedScanAnimationSeverityLevel;
+ private int mQueuedIconAnimationSeverityLevel;
+ private int mSettledSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN;
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
@@ -89,13 +91,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
summaryTextView.setText(mStatus.getSummary());
}
rescanButton = updateRescanButtonUi(rescanButton, pendingActionsRescanButton);
- updateRescanButtonVisibility(rescanButton);
-
- if (!mRefreshRunning) {
- statusImage.setImageResource(toStatusImageResId(mStatus.getSeverityLevel()));
- } else {
- rescanButton.setEnabled(false);
- }
+ setRescanButtonState(rescanButton);
int contentDescriptionResId =
R.string.safety_status_preference_title_and_summary_content_description;
@@ -117,21 +113,41 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
viewModel.getInteractionLogger().record(Action.SCAN_INITIATED);
});
- int refreshStatus = mStatus.getRefreshStatus();
- boolean inRefreshStatus =
- refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS
- || refreshStatus
- == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS;
- if (inRefreshStatus && !mRefreshRunning) {
- startScanningAnimation(statusImage, rescanButton);
- mRefreshRunning = true;
- } else if (!inRefreshStatus && mRefreshRunning && !mRefreshEnding) {
- mRefreshEnding = true;
+ updateStatusIcon(statusImage, rescanButton);
+ }
+
+ private void updateStatusIcon(ImageView statusImage, View rescanButton) {
+ int severityLevel = mStatus.getSeverityLevel();
+
+ boolean isRefreshing = isRefreshInProgress();
+ boolean shouldStartScanAnimation = isRefreshing && !mIsScanAnimationRunning;
+ boolean shouldEndScanAnimation = !isRefreshing && mIsScanAnimationRunning;
+ boolean shouldChangeIcon = mSettledSeverityLevel != severityLevel;
+
+ if (shouldStartScanAnimation && !mIsIconChangeAnimationRunning) {
+ startScanningAnimation(statusImage);
+ } else if (shouldStartScanAnimation) {
+ mQueuedScanAnimationSeverityLevel = severityLevel;
+ } else if (shouldEndScanAnimation) {
endScanningAnimation(statusImage, rescanButton);
+ } else if (shouldChangeIcon && !mIsScanAnimationRunning) {
+ startIconChangeAnimation(statusImage);
+ } else if (shouldChangeIcon) {
+ mQueuedIconAnimationSeverityLevel = severityLevel;
+ } else if (!mIsScanAnimationRunning && !mIsIconChangeAnimationRunning) {
+ setSettledStatus(statusImage);
}
}
- private void startScanningAnimation(ImageView statusImage, View rescanButton) {
+ private boolean isRefreshInProgress() {
+ int refreshStatus = mStatus.getRefreshStatus();
+ return refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS
+ || refreshStatus
+ == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS;
+ }
+
+ private void startScanningAnimation(ImageView statusImage) {
+ mIsScanAnimationRunning = true;
int currentSeverityLevel = mStatus.getSeverityLevel();
statusImage.setImageResource(
StatusAnimationResolver.getScanningStartAnimation(currentSeverityLevel));
@@ -148,7 +164,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
- if (mRefreshRunning) {
+ if (mIsScanAnimationRunning && isRefreshInProgress()) {
scanningAnim.start();
} else {
scanningAnim.clearAnimationCallbacks();
@@ -159,9 +175,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
}
});
animation.start();
- mCurrentSeverityLevel = currentSeverityLevel;
- updateRescanButtonVisibility(rescanButton);
- rescanButton.setEnabled(false);
}
private void endScanningAnimation(ImageView statusImage, View rescanButton) {
@@ -177,7 +190,7 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
return;
}
- int scanningSeverityLevel = mCurrentSeverityLevel;
+ int scanningSeverityLevel = mSettledSeverityLevel;
animatedStatusDrawable.clearAnimationCallbacks();
animatedStatusDrawable.registerAnimationCallback(
new Animatable2.AnimationCallback() {
@@ -202,12 +215,50 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
}
private void finishScanAnimation(ImageView statusImage, View rescanButton) {
- statusImage.setImageResource(toStatusImageResId(mStatus.getSeverityLevel()));
- mCurrentSeverityLevel = mStatus.getSeverityLevel();
- mRefreshRunning = false;
- mRefreshEnding = false;
- rescanButton.setEnabled(true);
- updateRescanButtonVisibility(rescanButton);
+ mIsScanAnimationRunning = false;
+ setRescanButtonState(rescanButton);
+ setSettledStatus(statusImage);
+ handleQueuedAction(statusImage);
+ }
+
+ private void startIconChangeAnimation(ImageView statusImage) {
+ int changeAnimationResId =
+ StatusAnimationResolver.getStatusChangeAnimation(
+ mSettledSeverityLevel,
+ mStatus.getSeverityLevel());
+ if (changeAnimationResId == 0) {
+ setSettledStatus(statusImage);
+ return;
+ }
+ mIsIconChangeAnimationRunning = true;
+ statusImage.setImageResource(changeAnimationResId);
+ AnimatedVectorDrawable animation = (AnimatedVectorDrawable) statusImage.getDrawable();
+ animation.clearAnimationCallbacks();
+ animation.registerAnimationCallback(
+ new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ mIsIconChangeAnimationRunning = false;
+ setSettledStatus(statusImage);
+ handleQueuedAction(statusImage);
+ }
+ });
+ animation.start();
+ }
+
+ private void setSettledStatus(ImageView statusImage) {
+ mSettledSeverityLevel = mStatus.getSeverityLevel();
+ statusImage.setImageResource(toStatusImageResId(mSettledSeverityLevel));
+ }
+
+ private void handleQueuedAction(ImageView statusImage) {
+ if (mQueuedScanAnimationSeverityLevel != 0) {
+ mQueuedScanAnimationSeverityLevel = 0;
+ startScanningAnimation(statusImage);
+ } else if (mQueuedIconAnimationSeverityLevel != 0) {
+ mQueuedIconAnimationSeverityLevel = 0;
+ startIconChangeAnimation(statusImage);
+ }
}
/**
@@ -231,8 +282,9 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
notifyChanged();
}
- void setHasIssues(boolean hasIssues) {
- mHasIssues = hasIssues;
+ void setSafetyData(SafetyCenterData data) {
+ mHasIssues = data.getIssues().size() > 0;
+ mStatus = data.getStatus();
notifyChanged();
}
@@ -254,12 +306,13 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
notifyChanged();
}
- private void updateRescanButtonVisibility(View rescanButton) {
+ private void setRescanButtonState(View rescanButton) {
rescanButton.setVisibility(
mStatus.getSeverityLevel() != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK
|| mHasIssues
? View.GONE
: View.VISIBLE);
+ rescanButton.setEnabled(!isRefreshInProgress());
}
private static int toStatusImageResId(int overallSeverityLevel) {
@@ -287,7 +340,11 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
@Override
public boolean hasSameContents(@NonNull Preference preference) {
- return preference instanceof SafetyStatusPreference
- && Objects.equals(mStatus, (((SafetyStatusPreference) preference).mStatus));
+ if (!(preference instanceof SafetyStatusPreference)) {
+ return false;
+ }
+ SafetyStatusPreference other = (SafetyStatusPreference) preference;
+ return Objects.equals(mStatus, other.mStatus)
+ && mHasIssues == other.mHasIssues;
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
index af6a95605..55288564c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StatusAnimationResolver.kt
@@ -126,4 +126,24 @@ object StatusAnimationResolver {
}
}
}
+
+ @JvmStatic
+ fun getStatusChangeAnimation(fromSeverity: Int, toSeverity: Int): Int =
+ if (fromSeverity == toSeverity &&
+ fromSeverity != SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) {
+ 0
+ } else when (fromSeverity) {
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK ->
+ R.drawable.safety_status_info_to_info_anim
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION ->
+ R.drawable.safety_status_recommend_to_info_anim
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING -> {
+ if (toSeverity == SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK) {
+ R.drawable.safety_status_warn_to_info_anim
+ } else {
+ R.drawable.safety_status_warn_to_recommend_anim
+ }
+ }
+ else -> 0
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
index 397e61832..914a3d88b 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
@@ -25,6 +25,8 @@ import android.safetycenter.SafetyCenterErrorDetails
import android.safetycenter.SafetyCenterIssue
import android.safetycenter.SafetyCenterManager
import android.safetycenter.config.SafetySource
+import android.util.Log
+import androidx.annotation.MainThread
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getMainExecutor
import androidx.fragment.app.Fragment
@@ -40,7 +42,8 @@ import java.util.concurrent.atomic.AtomicBoolean
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
- override val safetyCenterLiveData: LiveData<SafetyCenterData> by this::_safetyCenterLiveData
+ private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName
+ override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData
override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData
private val _safetyCenterLiveData = SafetyCenterLiveData()
@@ -86,6 +89,10 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
safetyCenterManager.executeSafetyCenterIssueAction(issue.id, action.id)
}
+ override fun markIssueResolvedUiCompleted(issueId: IssueId) {
+ _safetyCenterLiveData.markIssueResolvedUiCompleted(issueId)
+ }
+
override fun rescan() {
safetyCenterManager.refreshSafetySources(
SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK)
@@ -117,29 +124,121 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
}
inner class SafetyCenterLiveData :
- MutableLiveData<SafetyCenterData>(), SafetyCenterManager.OnSafetyCenterDataChangedListener {
+ MutableLiveData<SafetyCenterUiData>(),
+ SafetyCenterManager.OnSafetyCenterDataChangedListener {
+
+ // Managing the data queue isn't designed to support multithreading. Any methods that
+ // manipulate it, or the inFlight or resolved issues lists should only be called on the
+ // main thread, and are marked accordingly.
+ private val safetyCenterDataQueue = ArrayDeque<SafetyCenterData>()
+ private var currentInFlightIssues = mapOf<IssueId, ActionId>()
+ private val currentResolvedIssues = mutableMapOf<IssueId, ActionId>()
override fun onActive() {
safetyCenterManager.addOnSafetyCenterDataChangedListener(
- getMainExecutor(app.applicationContext), this)
+ getMainExecutor(app.applicationContext), this)
super.onActive()
}
override fun onInactive() {
safetyCenterManager.removeOnSafetyCenterDataChangedListener(this)
+
+ // Remove all the tracked state and start from scratch when active again.
+ currentInFlightIssues = mapOf()
+ currentResolvedIssues.clear()
+ safetyCenterDataQueue.clear()
super.onInactive()
}
+ @MainThread
override fun onSafetyCenterDataChanged(data: SafetyCenterData) {
- value = data
+ safetyCenterDataQueue.addLast(data)
+ maybeProcessDataToNextResolvedIssues()
}
override fun onError(errorDetails: SafetyCenterErrorDetails) {
_errorLiveData.value = errorDetails
}
+
+ @MainThread
+ private fun maybeProcessDataToNextResolvedIssues() {
+ // Only process data updates while we aren't waiting for issue resolution animations
+ // to complete.
+ if (currentResolvedIssues.isNotEmpty()) {
+ Log.d(
+ TAG,
+ "Received SafetyCenterData while issue resolution animations" +
+ " occurring. Will update UI with new data soon.")
+ return
+ }
+
+ while (safetyCenterDataQueue.isNotEmpty() && currentResolvedIssues.isEmpty()) {
+ val nextSafetyCenterData = safetyCenterDataQueue.first()
+
+ // Calculate newly resolved issues by diffing the tracked in-flight issues and the
+ // current update. Resolved issues are formerly in-flight issues that no longer
+ // appear in a subsequent SafetyCenterData update.
+ val nextResolvedIssues: Map<IssueId, ActionId> =
+ determineResolvedIssues(nextSafetyCenterData, currentInFlightIssues)
+
+ // Save the set of in-flight issues to diff against the next data update.
+ currentInFlightIssues = nextSafetyCenterData.getInFlightIssues()
+
+ if (nextResolvedIssues.isEmpty()) {
+ sendNextData()
+ } else {
+ currentResolvedIssues.putAll(nextResolvedIssues)
+ sendResolvedIssuesAndCurrentData()
+ }
+ }
+ }
+
+ private fun determineResolvedIssues(
+ incomingData: SafetyCenterData,
+ inFlightIssues: Map<IssueId, ActionId>
+ ): Map<IssueId, ActionId> {
+ // Any previously in-flight issue that does not appear in the incoming SafetyCenterData
+ // is considered resolved.
+ val issueIdSet: Set<IssueId> = incomingData.issues.map { issue -> issue.id }.toSet()
+ return inFlightIssues.filterNot { issue -> issueIdSet.contains(issue.key) }
+ }
+
+ private fun sendNextData() {
+ value = SafetyCenterUiData(safetyCenterDataQueue.removeFirst())
+ }
+
+ private fun sendResolvedIssuesAndCurrentData() {
+ val currentData = value?.safetyCenterData
+ if (currentData == null || currentResolvedIssues.isEmpty()) {
+ // There can only be resolved issues after receiving data with in-flight issues,
+ // so we should always have already sent data here.
+ throw IllegalArgumentException("No current data or no resolved issues")
+ }
+
+ // The current SafetyCenterData still contains the resolved SafetyCenterIssue objects.
+ // Send it with the resolved IDs so the UI can generate the correct preferences and
+ // trigger the right animations for issue resolution.
+ value = SafetyCenterUiData(currentData, currentResolvedIssues)
+ }
+
+ @MainThread
+ fun markIssueResolvedUiCompleted(issueId: IssueId) {
+ currentResolvedIssues.remove(issueId)
+ maybeProcessDataToNextResolvedIssues()
+ }
}
}
+private fun SafetyCenterData.getInFlightIssues(): Map<IssueId, ActionId> =
+ issues
+ .map { issue ->
+ issue.actions
+ .filter { it.isInFlight }
+ .map { issue.id to it.id }
+ }
+ .flatten()
+ .toMap()
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class LiveSafetyCenterViewModelFactory(private val app: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
index 1926a38f1..349f6822c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
@@ -31,7 +31,7 @@ import com.android.permissioncontroller.safetycenter.ui.NavigationSource
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
abstract class SafetyCenterViewModel(protected val app: Application) : AndroidViewModel(app) {
- abstract val safetyCenterLiveData: LiveData<SafetyCenterData>
+ abstract val safetyCenterUiLiveData: LiveData<SafetyCenterUiData>
abstract val errorLiveData: LiveData<SafetyCenterErrorDetails>
abstract val interactionLogger: InteractionLogger
@@ -39,6 +39,14 @@ abstract class SafetyCenterViewModel(protected val app: Application) : AndroidVi
abstract fun executeIssueAction(issue: SafetyCenterIssue, action: SafetyCenterIssue.Action)
+ /**
+ * Marks a resolved [SafetyCenterIssue] as fully complete, meaning the resolution success
+ * message has been shown
+ *
+ * @param issueId Resolved issue that has completed its UI update and view can be removed
+ */
+ abstract fun markIssueResolvedUiCompleted(issueId: IssueId)
+
abstract fun rescan()
abstract fun clearError()
@@ -52,3 +60,12 @@ abstract class SafetyCenterViewModel(protected val app: Application) : AndroidVi
abstract fun changingConfigurations()
}
+
+typealias IssueId = String
+
+typealias ActionId = String
+
+data class SafetyCenterUiData(
+ val safetyCenterData: SafetyCenterData,
+ val resolvedIssues: Map<IssueId, ActionId> = emptyMap()
+)
diff --git a/SafetyCenter/Resources/shared_res/values/strings.xml b/SafetyCenter/Resources/shared_res/values/strings.xml
index 250ea27e7..c5b02b3f8 100644
--- a/SafetyCenter/Resources/shared_res/values/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values/strings.xml
@@ -35,15 +35,15 @@
<string name="overall_severity_level_ok_review_summary">Check settings list</string>
<!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put them at risk [CHAR LIMIT=35] -->
- <string name="overall_severity_level_recommendation_title">Device may be at risk</string>
+ <string name="overall_severity_level_device_recommendation_title">Device may be at risk</string>
<!-- Title for the overall Safety Center status when the user security and privacy signals are putting them at risk [CHAR LIMIT=35] -->
- <string name="overall_severity_level_critical_warning_title">Device is at risk</string>
+ <string name="overall_severity_level_critical_device_warning_title">Device is at risk</string>
- <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their personal safety at risk [CHAR LIMIT=35] -->
+ <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their general safety at risk [CHAR LIMIT=35] -->
<string name="overall_severity_level_safety_recommendation_title">You may be at risk</string>
- <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their personal safety at risk [CHAR LIMIT=35] -->
+ <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their general safety at risk [CHAR LIMIT=35] -->
<string name="overall_severity_level_critical_safety_warning_title">You are at risk</string>
<!-- Summary for the overall Safety Center status when the user security and privacy signals contain alerts that could range from minor suggestions to warnings or critical issues. The number of alerts is given as a parameter [CHAR LIMIT=60] -->
diff --git a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
index a7dc770cb..727716b97 100644
--- a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
+++ b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
@@ -47,6 +47,7 @@ import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyCenterManager.RefreshReason;
import android.safetycenter.SafetyCenterManager.RefreshRequestType;
import android.util.Log;
+import android.util.SparseArray;
import androidx.annotation.RequiresApi;
@@ -110,53 +111,30 @@ final class SafetyCenterBroadcastDispatcher {
// rely on SafetyCenterManager#isSafetyCenterEnabled()?
void sendEnabledChanged(@NonNull List<Broadcast> broadcasts) {
BroadcastOptions broadcastOptions = createBroadcastOptions();
- List<UserProfileGroup> userProfileGroups =
- UserProfileGroup.getAllUserProfileGroups(mContext);
+ List<UserProfileGroup> profileGroups = UserProfileGroup.getAllUserProfileGroups(mContext);
+ // The same ENABLED reason is used here for both enable and disable events. It is not sent
+ // externally and is only used internally to filter safety sources in the methods of the
+ // Broadcast class:
+ int refreshReason = REFRESH_REASON_SAFETY_CENTER_ENABLED;
for (int i = 0; i < broadcasts.size(); i++) {
Broadcast broadcast = broadcasts.get(i);
- Intent broadcastIntent =
- createEnabledChangedBroadcastIntent(broadcast.getPackageName());
-
- for (int j = 0; j < userProfileGroups.size(); j++) {
- UserProfileGroup userProfileGroup = userProfileGroups.get(j);
-
- List<String> profileParentSourceIds =
- broadcast.getSourceIdsForProfileParent(
- REFRESH_REASON_SAFETY_CENTER_ENABLED);
- if (!profileParentSourceIds.isEmpty()) {
- int profileParentUserId = userProfileGroup.getProfileParentUserId();
-
- sendBroadcast(
- broadcastIntent,
- UserHandle.of(profileParentUserId),
- SEND_SAFETY_CENTER_UPDATE,
- broadcastOptions);
- }
+ Intent intent = createExplicitEnabledChangedIntent(broadcast.getPackageName());
+
+ for (int j = 0; j < profileGroups.size(); j++) {
+ UserProfileGroup profileGroup = profileGroups.get(j);
+ SparseArray<List<String>> userIdsToSourceIds =
+ getUserIdsToSourceIds(broadcast, profileGroup, refreshReason);
- List<String> managedProfilesSourceIds =
- broadcast.getSourceIdsForManagedProfiles(
- REFRESH_REASON_SAFETY_CENTER_ENABLED);
- if (!managedProfilesSourceIds.isEmpty()) {
- int[] managedRunningProfilesUserIds =
- userProfileGroup.getManagedRunningProfilesUserIds();
- for (int k = 0; k < managedRunningProfilesUserIds.length; k++) {
- int managedRunningProfileUserId = managedRunningProfilesUserIds[k];
- sendBroadcast(
- broadcastIntent,
- UserHandle.of(managedRunningProfileUserId),
- SEND_SAFETY_CENTER_UPDATE,
- broadcastOptions);
- }
+ for (int k = 0; k < userIdsToSourceIds.size(); k++) {
+ int userId = userIdsToSourceIds.keyAt(k);
+ sendBroadcastIfResolves(intent, UserHandle.of(userId), broadcastOptions);
}
}
}
- sendBroadcast(
- createEnabledChangedBroadcastIntent(),
- UserHandle.SYSTEM,
- READ_SAFETY_CENTER_STATUS,
- null);
+ Intent implicitIntent = createImplicitEnabledChangedIntent();
+ sendBroadcast(implicitIntent, UserHandle.SYSTEM, READ_SAFETY_CENTER_STATUS, null);
}
private void sendRefreshSafetySourcesBroadcast(
@@ -166,77 +144,47 @@ final class SafetyCenterBroadcastDispatcher {
@NonNull UserProfileGroup userProfileGroup,
@NonNull String broadcastId) {
int requestType = toRefreshRequestType(refreshReason);
- List<String> profileParentSourceIds = broadcast.getSourceIdsForProfileParent(refreshReason);
- if (!profileParentSourceIds.isEmpty()) {
- int profileParentUserId = userProfileGroup.getProfileParentUserId();
- Intent broadcastIntent =
- createRefreshSafetySourcesBroadcastIntent(
- requestType,
- broadcast.getPackageName(),
- profileParentSourceIds,
- broadcastId);
-
- boolean sent =
- sendBroadcast(
- broadcastIntent,
- UserHandle.of(profileParentUserId),
- SEND_SAFETY_CENTER_UPDATE,
- broadcastOptions);
-
+ String packageName = broadcast.getPackageName();
+ SparseArray<List<String>> userIdsToSourceIds =
+ getUserIdsToSourceIds(broadcast, userProfileGroup, refreshReason);
+
+ for (int i = 0; i < userIdsToSourceIds.size(); i++) {
+ int userId = userIdsToSourceIds.keyAt(i);
+ List<String> sourceIds = userIdsToSourceIds.valueAt(i);
+ Intent intent = createRefreshIntent(requestType, packageName, sourceIds, broadcastId);
+ boolean sent = sendBroadcastIfResolves(intent, UserHandle.of(userId), broadcastOptions);
if (sent) {
- mRefreshTracker.reportSourceRefreshesInFlight(
- broadcastId, profileParentSourceIds, profileParentUserId);
- }
- }
- List<String> managedProfilesSourceIds =
- broadcast.getSourceIdsForManagedProfiles(refreshReason);
- if (!managedProfilesSourceIds.isEmpty()) {
- int[] managedRunningProfilesUserIds =
- userProfileGroup.getManagedRunningProfilesUserIds();
- for (int i = 0; i < managedRunningProfilesUserIds.length; i++) {
- int managedRunningProfilesUserId = managedRunningProfilesUserIds[i];
- Intent broadcastIntent =
- createRefreshSafetySourcesBroadcastIntent(
- requestType,
- broadcast.getPackageName(),
- managedProfilesSourceIds,
- broadcastId);
-
- boolean sent =
- sendBroadcast(
- broadcastIntent,
- UserHandle.of(managedRunningProfilesUserId),
- SEND_SAFETY_CENTER_UPDATE,
- broadcastOptions);
-
- if (sent) {
- mRefreshTracker.reportSourceRefreshesInFlight(
- broadcastId, managedProfilesSourceIds, managedRunningProfilesUserId);
- }
+ mRefreshTracker.reportSourceRefreshesInFlight(broadcastId, sourceIds, userId);
}
}
}
- private boolean sendBroadcast(
- @NonNull Intent broadcastIntent,
+ private boolean sendBroadcastIfResolves(
+ @NonNull Intent intent,
@NonNull UserHandle userHandle,
- @NonNull String permission,
@Nullable BroadcastOptions broadcastOptions) {
- if (!doesBroadcastResolve(broadcastIntent)) {
- Log.w(TAG, "No receiver for intent targeting " + broadcastIntent.getPackage());
+ if (!doesBroadcastResolve(intent)) {
+ Log.w(TAG, "No receiver for intent targeting " + intent.getPackage());
return false;
}
- Log.v(TAG, "Found receiver for intent targeting " + broadcastIntent.getPackage());
+ Log.v(TAG, "Found receiver for intent targeting " + intent.getPackage());
+ sendBroadcast(intent, userHandle, SEND_SAFETY_CENTER_UPDATE, broadcastOptions);
+ return true;
+ }
+ private void sendBroadcast(
+ @NonNull Intent intent,
+ @NonNull UserHandle userHandle,
+ @NonNull String permission,
+ @Nullable BroadcastOptions broadcastOptions) {
// The following operation requires the INTERACT_ACROSS_USERS permission.
final long callingId = Binder.clearCallingIdentity();
try {
mContext.sendBroadcastAsUser(
- broadcastIntent,
+ intent,
userHandle,
permission,
broadcastOptions == null ? null : broadcastOptions.toBundle());
- return true;
} finally {
Binder.restoreCallingIdentity(callingId);
}
@@ -247,25 +195,25 @@ final class SafetyCenterBroadcastDispatcher {
}
@NonNull
- private static Intent createEnabledChangedBroadcastIntent(@NonNull String packageName) {
- return createEnabledChangedBroadcastIntent().setPackage(packageName);
+ private static Intent createExplicitEnabledChangedIntent(@NonNull String packageName) {
+ return createImplicitEnabledChangedIntent().setPackage(packageName);
}
@NonNull
- private static Intent createEnabledChangedBroadcastIntent() {
+ private static Intent createImplicitEnabledChangedIntent() {
return createBroadcastIntent(ACTION_SAFETY_CENTER_ENABLED_CHANGED);
}
@NonNull
- private static Intent createRefreshSafetySourcesBroadcastIntent(
+ private static Intent createRefreshIntent(
@RefreshRequestType int requestType,
@NonNull String packageName,
@NonNull List<String> sourceIdsToRefresh,
@NonNull String broadcastId) {
+ String[] sourceIdsArray = sourceIdsToRefresh.toArray(new String[0]);
return createBroadcastIntent(ACTION_REFRESH_SAFETY_SOURCES)
.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE, requestType)
- .putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS, sourceIdsToRefresh.toArray(new String[0]))
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, sourceIdsArray)
.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, broadcastId)
.setPackage(packageName);
}
@@ -307,4 +255,39 @@ final class SafetyCenterBroadcastDispatcher {
}
throw new IllegalArgumentException("Unexpected refresh reason: " + refreshReason);
}
+
+ /**
+ * Returns a flattened mapping from user IDs to lists of source IDs for those users. The map is
+ * in the form of a {@link SparseArray} where the int keys are user IDs and the values are the
+ * lists of source IDs.
+ *
+ * <p>The set of user IDs (keys) is the profile parent user ID of {@code userProfileGroup} plus
+ * the (possibly empty) set of running managed profile user IDs in that group.
+ *
+ * <p>Every value present is a non-empty list, but the overall result may be empty.
+ */
+ private static SparseArray<List<String>> getUserIdsToSourceIds(
+ @NonNull Broadcast broadcast,
+ @NonNull UserProfileGroup userProfileGroup,
+ @RefreshReason int refreshReason) {
+ int[] managedProfileIds = userProfileGroup.getManagedRunningProfilesUserIds();
+ SparseArray<List<String>> result = new SparseArray<>(managedProfileIds.length + 1);
+
+ List<String> profileParentSources = broadcast.getSourceIdsForProfileParent(refreshReason);
+ if (!profileParentSources.isEmpty()) {
+ result.put(userProfileGroup.getProfileParentUserId(), profileParentSources);
+ }
+
+ if (managedProfileIds.length > 0) {
+ List<String> managedProfileSources =
+ broadcast.getSourceIdsForManagedProfiles(refreshReason);
+ if (!managedProfileSources.isEmpty()) {
+ for (int i = 0; i < managedProfileIds.length; i++) {
+ result.put(managedProfileIds[i], managedProfileSources);
+ }
+ }
+ }
+
+ return result;
+ }
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java
index ac1d44f12..c83734b3c 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java
@@ -443,8 +443,8 @@ final class SafetyCenterDataTracker {
new SafetyCenterStatus.Builder(
getSafetyCenterStatusTitle(
SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN,
+ new ArrayList<>(),
SafetyCenterStatus.REFRESH_STATUS_NONE,
- new ArraySet<>(),
false),
getSafetyCenterStatusSummary(
SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN,
@@ -770,8 +770,7 @@ final class SafetyCenterDataTracker {
@NonNull UserProfileGroup userProfileGroup) {
int safetyCenterOverallSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK;
int safetyCenterEntriesSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK;
- List<SafetyCenterIssue> safetyCenterIssues = new ArrayList<>();
- ArraySet<Integer> allCurrentIssueCategories = new ArraySet<>();
+ List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories = new ArrayList<>();
List<SafetyCenterEntryOrGroup> safetyCenterEntryOrGroups = new ArrayList<>();
List<SafetyCenterStaticEntryGroup> safetyCenterStaticEntryGroups = new ArrayList<>();
SafetyCenterOverallStatusErrorState safetyCenterOverallStatusErrorState =
@@ -784,8 +783,7 @@ final class SafetyCenterDataTracker {
Math.max(
safetyCenterOverallSeverityLevel,
addSafetyCenterIssues(
- safetyCenterIssues,
- allCurrentIssueCategories,
+ safetyCenterIssuesWithCategories,
safetySourcesGroup,
userProfileGroup));
int safetySourcesGroupType = safetySourcesGroup.getType();
@@ -820,14 +818,21 @@ final class SafetyCenterDataTracker {
boolean hasSettingsToReview =
safetyCenterEntriesSeverityLevel > safetyCenterOverallSeverityLevel
|| safetyCenterOverallStatusErrorState.mHasAtLeastOneUserVisibleError;
- safetyCenterIssues.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING);
+ safetyCenterIssuesWithCategories.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING);
+
+ List<SafetyCenterIssue> safetyCenterIssues =
+ new ArrayList<>(safetyCenterIssuesWithCategories.size());
+ for (int i = 0; i < safetyCenterIssuesWithCategories.size(); i++) {
+ safetyCenterIssues.add(safetyCenterIssuesWithCategories.get(i).getSafetyCenterIssue());
+ }
+
int refreshStatus = mSafetyCenterRefreshTracker.getRefreshStatus();
return new SafetyCenterData(
new SafetyCenterStatus.Builder(
getSafetyCenterStatusTitle(
safetyCenterOverallSeverityLevel,
+ safetyCenterIssuesWithCategories,
refreshStatus,
- allCurrentIssueCategories,
hasSettingsToReview),
getSafetyCenterStatusSummary(
safetyCenterOverallSeverityLevel,
@@ -844,8 +849,7 @@ final class SafetyCenterDataTracker {
@SafetyCenterStatus.OverallSeverityLevel
private int addSafetyCenterIssues(
- @NonNull List<SafetyCenterIssue> safetyCenterIssues,
- @NonNull ArraySet<Integer> allCurrentIssueCategories,
+ @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
@NonNull SafetySourcesGroup safetySourcesGroup,
@NonNull UserProfileGroup userProfileGroup) {
int safetyCenterIssuesOverallSeverityLevel = SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK;
@@ -861,8 +865,7 @@ final class SafetyCenterDataTracker {
Math.max(
safetyCenterIssuesOverallSeverityLevel,
addSafetyCenterIssues(
- safetyCenterIssues,
- allCurrentIssueCategories,
+ safetyCenterIssuesWithCategories,
safetySource,
userProfileGroup.getProfileParentUserId()));
@@ -879,8 +882,7 @@ final class SafetyCenterDataTracker {
Math.max(
safetyCenterIssuesOverallSeverityLevel,
addSafetyCenterIssues(
- safetyCenterIssues,
- allCurrentIssueCategories,
+ safetyCenterIssuesWithCategories,
safetySource,
managedRunningProfileUserId));
}
@@ -891,8 +893,7 @@ final class SafetyCenterDataTracker {
@SafetyCenterStatus.OverallSeverityLevel
private int addSafetyCenterIssues(
- @NonNull List<SafetyCenterIssue> safetyCenterIssues,
- @NonNull ArraySet<Integer> allCurrentIssueCategories,
+ @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
@NonNull SafetySource safetySource,
@UserIdInt int userId) {
SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId);
@@ -917,9 +918,9 @@ final class SafetyCenterDataTracker {
safetyCenterIssuesOverallSeverityLevel,
toSafetyCenterStatusOverallSeverityLevel(
safetySourceIssue.getSeverityLevel()));
- safetyCenterIssues.add(safetyCenterIssue);
-
- allCurrentIssueCategories.add(safetySourceIssue.getIssueCategory());
+ safetyCenterIssuesWithCategories.add(
+ SafetyCenterIssueWithCategory.create(
+ safetyCenterIssue, safetySourceIssue.getIssueCategory()));
}
return safetyCenterIssuesOverallSeverityLevel;
@@ -1047,10 +1048,9 @@ final class SafetyCenterDataTracker {
SafetyCenterEntryGroupId.newBuilder()
.setSafetySourcesGroupId(safetySourcesGroup.getId())
.build();
- CharSequence groupSummary = getSafetyCenterEntryGroupSummary(
- safetySourcesGroup,
- groupSafetyCenterEntryLevel,
- entries);
+ CharSequence groupSummary =
+ getSafetyCenterEntryGroupSummary(
+ safetySourcesGroup, groupSafetyCenterEntryLevel, entries);
safetyCenterEntryOrGroups.add(
new SafetyCenterEntryOrGroup(
new SafetyCenterEntryGroup.Builder(
@@ -1688,17 +1688,13 @@ final class SafetyCenterDataTracker {
private String getSafetyCenterStatusTitle(
@SafetyCenterStatus.OverallSeverityLevel int overallSeverityLevel,
+ @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
@SafetyCenterStatus.RefreshStatus int refreshStatus,
- @NonNull ArraySet<Integer> allCurrentIssueCategories,
boolean hasSettingsToReview) {
String refreshStatusTitle = getSafetyCenterRefreshStatusTitle(refreshStatus);
if (refreshStatusTitle != null) {
return refreshStatusTitle;
}
- boolean onlyAccountIssuesPresent =
- allCurrentIssueCategories.size() == 1
- && allCurrentIssueCategories.contains(
- SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT);
switch (overallSeverityLevel) {
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN:
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK:
@@ -1709,25 +1705,48 @@ final class SafetyCenterDataTracker {
return mSafetyCenterResourcesContext.getStringByName(
"overall_severity_level_ok_title");
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION:
- if (onlyAccountIssuesPresent) {
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_account_recommendation_title");
- }
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_recommendation_title");
+ return getStatusTitleFromIssueCategories(
+ safetyCenterIssuesWithCategories,
+ "overall_severity_level_device_recommendation_title",
+ "overall_severity_level_account_recommendation_title",
+ "overall_severity_level_safety_recommendation_title");
case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING:
- if (onlyAccountIssuesPresent) {
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_account_warning_title");
- }
- return mSafetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_warning_title");
+ return getStatusTitleFromIssueCategories(
+ safetyCenterIssuesWithCategories,
+ "overall_severity_level_critical_device_warning_title",
+ "overall_severity_level_critical_account_warning_title",
+ "overall_severity_level_critical_safety_warning_title");
}
Log.w(TAG, "Unexpected SafetyCenterStatus.OverallSeverityLevel: " + overallSeverityLevel);
return "";
}
+ @NonNull
+ private String getStatusTitleFromIssueCategories(
+ @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
+ @NonNull String deviceResourceName,
+ @NonNull String accountResourceName,
+ @NonNull String generalResourceName) {
+ String generalString = mSafetyCenterResourcesContext.getStringByName(generalResourceName);
+ if (safetyCenterIssuesWithCategories.isEmpty()) {
+ Log.w(TAG, "No safety center issues found in a non-green status");
+ return generalString;
+ }
+ int issueCategory = safetyCenterIssuesWithCategories.get(0).getSafetyCenterIssueCategory();
+ switch (issueCategory) {
+ case SafetySourceIssue.ISSUE_CATEGORY_DEVICE:
+ return mSafetyCenterResourcesContext.getStringByName(deviceResourceName);
+ case SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT:
+ return mSafetyCenterResourcesContext.getStringByName(accountResourceName);
+ case SafetySourceIssue.ISSUE_CATEGORY_GENERAL:
+ return generalString;
+ }
+
+ Log.w(TAG, "Unexpected issueCategory found: " + issueCategory);
+ return generalString;
+ }
+
private String getSafetyCenterStatusSummary(
@SafetyCenterStatus.OverallSeverityLevel int overallSeverityLevel,
@SafetyCenterStatus.RefreshStatus int refreshStatus,
@@ -1834,15 +1853,48 @@ final class SafetyCenterDataTracker {
return SafetySourceKey.of(id.getSafetySourceId(), id.getUserId());
}
- /** A comparator to order {@link SafetyCenterIssue}s by severity level descending. */
+ /** Wrapper that encapsulates both {@link SafetyCenterIssue} and its category. */
+ private static final class SafetyCenterIssueWithCategory {
+ @NonNull private final SafetyCenterIssue mSafetyCenterIssue;
+ @SafetySourceIssue.IssueCategory private final int mSafetyCenterIssueCategory;
+
+ private SafetyCenterIssueWithCategory(
+ @NonNull SafetyCenterIssue safetyCenterIssue,
+ @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) {
+ this.mSafetyCenterIssue = safetyCenterIssue;
+ this.mSafetyCenterIssueCategory = safetyCenterIssueCategory;
+ }
+
+ @NonNull
+ public SafetyCenterIssue getSafetyCenterIssue() {
+ return mSafetyCenterIssue;
+ }
+
+ @SafetySourceIssue.IssueCategory
+ public int getSafetyCenterIssueCategory() {
+ return mSafetyCenterIssueCategory;
+ }
+
+ public static SafetyCenterIssueWithCategory create(
+ @NonNull SafetyCenterIssue safetyCenterIssue,
+ @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) {
+ return new SafetyCenterIssueWithCategory(safetyCenterIssue, safetyCenterIssueCategory);
+ }
+ }
+
+ /** A comparator to order {@link SafetyCenterIssueWithCategory} by severity level descending. */
private static final class SafetyCenterIssuesBySeverityDescending
- implements Comparator<SafetyCenterIssue> {
+ implements Comparator<SafetyCenterIssueWithCategory> {
SafetyCenterIssuesBySeverityDescending() {}
@Override
- public int compare(@NonNull SafetyCenterIssue left, @NonNull SafetyCenterIssue right) {
- return Integer.compare(right.getSeverityLevel(), left.getSeverityLevel());
+ public int compare(
+ @NonNull SafetyCenterIssueWithCategory left,
+ @NonNull SafetyCenterIssueWithCategory right) {
+ return Integer.compare(
+ right.getSafetyCenterIssue().getSeverityLevel(),
+ left.getSafetyCenterIssue().getSeverityLevel());
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java
index cd4c67960..687a2475f 100644
--- a/service/java/com/android/safetycenter/SafetyCenterFlags.java
+++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java
@@ -63,6 +63,9 @@ final class SafetyCenterFlags {
private static final String PROPERTY_UNTRACKED_SOURCES = "safety_center_untracked_sources";
+ private static final String PROPERTY_NO_BACKGROUND_REFRESH_SOURCES =
+ "safety_center_no_background_refresh_sources";
+
private static final Duration REFRESH_SOURCE_TIMEOUT_DEFAULT_DURATION = Duration.ofSeconds(10);
private static final Duration RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION =
@@ -90,6 +93,7 @@ final class SafetyCenterFlags {
printFlag(fout, PROPERTY_UNTRACKED_SOURCES, getUntrackedSourceIds());
printFlag(fout, PROPERTY_RESURFACE_ISSUE_MAX_COUNTS, getResurfaceIssueMaxCounts());
printFlag(fout, PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS, getResurfaceIssueDelaysMillis());
+ printFlag(fout, PROPERTY_NO_BACKGROUND_REFRESH_SOURCES, getNoBackgroundRefreshSourceIds());
fout.println();
}
@@ -110,7 +114,7 @@ final class SafetyCenterFlags {
* Returns whether we should show error entries for sources that timeout when refreshing them.
*/
static boolean getShowErrorEntriesOnTimeout() {
- return getBoolean(PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT, false);
+ return getBoolean(PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT, true);
}
/**
@@ -155,9 +159,16 @@ final class SafetyCenterFlags {
*/
@NonNull
static ArraySet<String> getUntrackedSourceIds() {
- String untrackedSourcesConfigString = getString(PROPERTY_UNTRACKED_SOURCES, "");
- String[] untrackedSourcesList = untrackedSourcesConfigString.split(",");
- return new ArraySet<>(untrackedSourcesList);
+ return getCommaSeparatedStrings(PROPERTY_UNTRACKED_SOURCES);
+ }
+
+ /**
+ * Returns the IDs of sources that should only be refreshed when Safety Center is on screen. We
+ * will refresh these sources only on page open and when the scan button is clicked.
+ */
+ @NonNull
+ static ArraySet<String> getNoBackgroundRefreshSourceIds() {
+ return getCommaSeparatedStrings(PROPERTY_NO_BACKGROUND_REFRESH_SOURCES);
}
/**
@@ -236,6 +247,11 @@ final class SafetyCenterFlags {
}
@NonNull
+ private static ArraySet<String> getCommaSeparatedStrings(@NonNull String property) {
+ return new ArraySet<>(getString(property, "").split(","));
+ }
+
+ @NonNull
private static String getString(@NonNull String property, @NonNull String defaultValue) {
// This call requires the READ_DEVICE_CONFIG permission.
final long callingId = Binder.clearCallingIdentity();
diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java
index 2cde66f73..ddb4802e5 100644
--- a/service/java/com/android/safetycenter/SafetyCenterService.java
+++ b/service/java/com/android/safetycenter/SafetyCenterService.java
@@ -57,7 +57,6 @@ import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceErrorDetails;
import android.safetycenter.SafetySourceIssue;
import android.safetycenter.config.SafetyCenterConfig;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -183,7 +182,7 @@ public final class SafetyCenterService extends SystemService {
mConfigAvailable = mSafetyCenterConfigReader.loadConfig();
if (mConfigAvailable) {
readSafetyCenterIssueCacheFileLocked();
- registerUserChangesReceiver();
+ new UserBroadcastReceiver().register(getContext());
}
}
}
@@ -929,54 +928,66 @@ public final class SafetyCenterService extends SystemService {
return mDeviceSupportsSafetyCenter && mConfigAvailable;
}
- private void registerUserChangesReceiver() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext()
- .registerReceiverForAllUsers(
- new BroadcastReceiver() {
- @Override
- public void onReceive(
- @NonNull Context context, @NonNull Intent intent) {
- if (TextUtils.equals(
- intent.getAction(), Intent.ACTION_USER_REMOVED)) {
- int userId =
- intent.getParcelableExtra(
- Intent.EXTRA_USER, UserHandle.class)
- .getIdentifier();
- onRemoveUser(userId);
- }
- if (TextUtils.equals(
- intent.getAction(),
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
- int userId =
- intent.getParcelableExtra(
- Intent.EXTRA_USER, UserHandle.class)
- .getIdentifier();
- onEnableQuietMode(userId);
- }
- if (TextUtils.equals(
- intent.getAction(),
- Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
- int userId =
- intent.getParcelableExtra(
- Intent.EXTRA_USER, UserHandle.class)
- .getIdentifier();
- onDisableQuietMode(userId);
- }
- }
- },
- intentFilter,
- null,
- null);
+ /**
+ * {@link BroadcastReceiver} which handles user and work profile related broadcasts that Safety
+ * Center is interested including quiet mode turning on/off and accounts being added/removed.
+ */
+ private final class UserBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "UserBroadcastReceiver";
+
+ @NonNull
+ void register(@NonNull Context context) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ context.registerReceiverForAllUsers(this, filter, null, null);
+ }
+
+ @Override
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "Received broadcast with null action!");
+ return;
+ }
+
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
+ if (userHandle == null) {
+ Log.w(TAG, "Received " + action + " broadcast missing user extra!");
+ return;
+ }
+
+ int userId = userHandle.getIdentifier();
+ Log.d(TAG, "Received " + action + " broadcast for user " + userId);
+
+ switch (action) {
+ case Intent.ACTION_USER_REMOVED:
+ case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+ removeUser(userId, true);
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ removeUser(userId, false);
+ // fall through!
+ case Intent.ACTION_USER_ADDED:
+ case Intent.ACTION_MANAGED_PROFILE_ADDED:
+ case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+ startRefreshingSafetySources(REFRESH_REASON_OTHER, userId);
+ break;
+ }
+ }
}
- private void onRemoveUser(@UserIdInt int userId) {
+ private void removeUser(@UserIdInt int userId, boolean clearDataPermanently) {
UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId);
synchronized (mApiLock) {
- mSafetyCenterDataTracker.clearForUser(userId);
+ if (clearDataPermanently) {
+ mSafetyCenterDataTracker.clearForUser(userId);
+ }
mSafetyCenterListeners.clearForUser(userId);
mSafetyCenterRefreshTracker.clearRefreshForUser(userId);
mSafetyCenterListeners.deliverUpdateForUserProfileGroup(userProfileGroup, true, null);
@@ -984,15 +995,6 @@ public final class SafetyCenterService extends SystemService {
}
}
- private void onEnableQuietMode(@UserIdInt int userId) {
- onRemoveUser(userId);
- startRefreshingSafetySources(REFRESH_REASON_OTHER, userId);
- }
-
- private void onDisableQuietMode(@UserIdInt int userId) {
- startRefreshingSafetySources(REFRESH_REASON_OTHER, userId);
- }
-
private void startRefreshingSafetySources(
@RefreshReason int refreshReason, @UserIdInt int userId) {
UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId);
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt
index 854456b5f..56bcd0591 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt
@@ -16,10 +16,12 @@
package android.safetycenter.cts
+import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
import android.content.Context
import android.os.Bundle
import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID
import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID
+import android.safetycenter.cts.testing.Coroutines.TIMEOUT_SHORT
import android.safetycenter.cts.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG
@@ -30,11 +32,16 @@ import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_SOURCES_CONFIG
import android.safetycenter.cts.testing.SafetyCenterCtsHelper
+import android.safetycenter.cts.testing.SafetyCenterFlags
import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import android.safetycenter.cts.testing.SafetySourceCtsData
import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.CRITICAL_ISSUE_ID
import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.RECOMMENDATION_ISSUE_ID
+import android.safetycenter.cts.testing.SafetySourceReceiver
+import android.safetycenter.cts.testing.SafetySourceReceiver.Companion.SafetySourceDataKey
+import android.safetycenter.cts.testing.SafetySourceReceiver.Companion.SafetySourceDataKey.Reason.RESOLVE_ACTION
import android.safetycenter.cts.testing.SettingsPackage.getSettingsPackageName
+import android.safetycenter.cts.testing.ShellPermissions.callWithShellPermissionIdentity
import android.safetycenter.cts.testing.UiTestHelper.assertSourceDataDisplayed
import android.safetycenter.cts.testing.UiTestHelper.assertSourceIssueDisplayed
import android.safetycenter.cts.testing.UiTestHelper.assertSourceIssueNotDisplayed
@@ -125,7 +132,7 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_displaysSafetyData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
- val dataToDisplay = safetySourceCtsData.criticalWithResolvingIssue
+ val dataToDisplay = safetySourceCtsData.criticalWithResolvingGeneralIssue
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
context.launchSafetyCenterActivity { assertSourceDataDisplayed(dataToDisplay) }
@@ -137,7 +144,7 @@ class SafetyCenterActivityTest {
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
context.launchSafetyCenterActivity {
- val dataToDisplay = safetySourceCtsData.recommendationWithIssue
+ val dataToDisplay = safetySourceCtsData.recommendationWithGeneralIssue
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
assertSourceDataDisplayed(dataToDisplay)
@@ -200,7 +207,7 @@ class SafetyCenterActivityTest {
fun issueCard_criticalIssue_hasContentDescriptions() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Alert. Critical issue title. Critical issue summary"))
@@ -241,14 +248,14 @@ class SafetyCenterActivityTest {
fun issueCard_confirmsDismissal_dismisses() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
waitFindObject(By.text("Dismiss this alert?"))
findButton("Dismiss").click()
- assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
findButton("Scan device")
}
}
@@ -257,7 +264,7 @@ class SafetyCenterActivityTest {
fun issueCard_confirmsDismissal_afterRotation_dismisses() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
@@ -276,7 +283,7 @@ class SafetyCenterActivityTest {
"Review your security and privacy settings any time to add more protection"))
findButton("Dismiss").click()
- assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
findButton("Scan device")
}
}
@@ -285,14 +292,14 @@ class SafetyCenterActivityTest {
fun issueCard_confirmsDismissal_cancels() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
waitFindObject(By.text("Dismiss this alert?"))
findButton("Cancel").click()
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
}
}
@@ -300,7 +307,7 @@ class SafetyCenterActivityTest {
fun issueCard_confirmsDismissal_afterRotation_cancels() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
@@ -313,7 +320,120 @@ class SafetyCenterActivityTest {
waitFindObject(By.text("Dismiss this alert?"))
findButton("Cancel").click()
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ }
+ }
+
+ @Test
+ fun issueCard_resolveIssue_successConfirmationShown_issueDismisses() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+ // Set the initial data for the source
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssueWithSuccessMessage)
+
+ // Clear the data when action is triggered to simulate resolution.
+ SafetySourceReceiver.safetySourceData[
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = null
+
+ context.launchSafetyCenterActivity {
+ callWithShellPermissionIdentity(
+ {
+ val action = safetySourceCtsData.criticalResolvingActionWithSuccessMessage
+ waitFindObject(By.text(action.label.toString())).click()
+
+ // Success message should show up if issue marked as resolved
+ val successMessage = action.successMessage.toString()
+ waitFindObject(By.text(successMessage))
+
+ // Wait for success message to go away, verify issue no longer displayed
+ waitTextNotDisplayed(successMessage)
+ assertSourceIssueNotDisplayed(
+ safetySourceCtsData.criticalResolvingIssueWithSuccessMessage)
+ },
+ SEND_SAFETY_CENTER_UPDATE)
+ }
+ }
+
+ @Test
+ fun issueCard_resolveIssue_noSuccessMessage_defaultSuccessMessageShown_issueDismisses() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+ // Set the initial data for the source
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+
+ // Clear the data when action is triggered to simulate resolution.
+ SafetySourceReceiver.safetySourceData[
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = null
+
+ context.launchSafetyCenterActivity {
+ callWithShellPermissionIdentity(
+ {
+ val action = safetySourceCtsData.criticalResolvingAction
+ waitFindObject(By.text(action.label.toString())).click()
+
+ // Default success message should show up if issue marked as resolved
+ waitFindObject(By.text("Complete"))
+
+ // Wait for success message to go away, verify issue no longer displayed
+ waitTextNotDisplayed("Complete")
+ assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ },
+ SEND_SAFETY_CENTER_UPDATE)
+ }
+ }
+
+ @Test
+ fun issueCard_resolvingInflightIssueFailed_issueRemains() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+ // Set the initial data for the source
+ val data = safetySourceCtsData.criticalWithResolvingGeneralIssue
+ safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+
+ // Respond with an error when the action is triggered
+ SafetySourceReceiver.safetySourceData[
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = null
+ SafetySourceReceiver.shouldReportSafetySourceError = true
+
+ context.launchSafetyCenterActivity {
+ callWithShellPermissionIdentity(
+ {
+ val action = safetySourceCtsData.criticalResolvingAction
+ waitFindObject(By.text(action.label.toString())).click()
+
+ // criticalResolvingAction does not define a success message, check for default
+ waitTextNotDisplayed("Complete")
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ },
+ SEND_SAFETY_CENTER_UPDATE)
+ }
+ }
+
+ @Test
+ fun issueCard_resolvingInFlightIssueTimesOut_issueRemains() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+ // Set the initial data for the source
+ val data = safetySourceCtsData.criticalWithResolvingGeneralIssue
+ safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+
+ SafetyCenterFlags.resolveActionTimeout = TIMEOUT_SHORT
+
+ // Set no data at all on the receiver, will ignore incoming call.
+
+ context.launchSafetyCenterActivity {
+ callWithShellPermissionIdentity(
+ {
+ val action = safetySourceCtsData.criticalResolvingAction
+ waitFindObject(By.text(action.label.toString())).click()
+
+ // criticalResolvingAction does not define a success message, check for default
+ waitTextNotDisplayed("Complete")
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ },
+ SEND_SAFETY_CENTER_UPDATE)
}
}
@@ -336,8 +456,10 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_fromQuickSettings_issuesExpanded() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
val bundle = Bundle()
@@ -345,8 +467,8 @@ class SafetyCenterActivityTest {
context.launchSafetyCenterActivity(bundle) {
// Verify cards expanded
waitTextNotDisplayed("See all alerts")
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -354,17 +476,19 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_fromNotification_targetIssueAlreadyFirstIssue() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
val bundle = Bundle()
bundle.putString(EXTRA_SAFETY_SOURCE_ID, SOURCE_ID_1)
bundle.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, CRITICAL_ISSUE_ID)
context.launchSafetyCenterActivity(bundle) {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
waitFindObject(By.text("See all alerts"))
- assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -372,7 +496,8 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_fromNotification_targetIssueSamePriorityAsFirstIssue_reorderedFirstIssue() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.criticalWithRedirectingIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
@@ -382,7 +507,7 @@ class SafetyCenterActivityTest {
context.launchSafetyCenterActivity(bundle) {
assertSourceIssueDisplayed(safetySourceCtsData.criticalRedirectingIssue)
waitFindObject(By.text("See all alerts"))
- assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -390,16 +515,18 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_fromNotification_targetLowerPriorityAsFirstIssue_reorderedSecondIssue() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
val bundle = Bundle()
bundle.putString(EXTRA_SAFETY_SOURCE_ID, SOURCE_ID_2)
bundle.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, RECOMMENDATION_ISSUE_ID)
context.launchSafetyCenterActivity(bundle) {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
waitFindObject(By.text("See all alerts"))
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
@@ -408,17 +535,19 @@ class SafetyCenterActivityTest {
@Test
fun launchActivity_fromNotification_targetIssueNotFound() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
val bundle = Bundle()
bundle.putString(EXTRA_SAFETY_SOURCE_ID, SOURCE_ID_2)
bundle.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, CRITICAL_ISSUE_ID)
context.launchSafetyCenterActivity(bundle) {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
waitFindObject(By.text("See all alerts"))
- assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -427,10 +556,10 @@ class SafetyCenterActivityTest {
fun moreIssuesCard_underMaxShownIssues_noMoreIssuesCard() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
context.launchSafetyCenterActivity {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
waitTextNotDisplayed("See all alerts")
}
}
@@ -438,14 +567,16 @@ class SafetyCenterActivityTest {
@Test
fun moreIssuesCard_moreIssuesCardShown_additionalIssueCardsCollapsed() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
waitFindObject(By.text("See all alerts"))
- assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueNotDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -453,19 +584,21 @@ class SafetyCenterActivityTest {
@Test
fun moreIssuesCard_expandAdditionalIssueCards() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
expandMoreIssuesCard()
// Verify cards expanded
waitTextNotDisplayed("See all alerts")
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -473,8 +606,10 @@ class SafetyCenterActivityTest {
@Test
fun moreIssuesCard_rotation_cardsStillExpanded() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
@@ -485,8 +620,8 @@ class SafetyCenterActivityTest {
// Verify cards initially expanded
waitTextNotDisplayed("See all alerts")
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueDisplayed(safetySourceCtsData.informationIssue)
// Device rotation to trigger usage of savedinstancestate via config update
@@ -494,8 +629,8 @@ class SafetyCenterActivityTest {
// Verify cards remain expanded
waitTextNotDisplayed("See all alerts")
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
@@ -503,16 +638,18 @@ class SafetyCenterActivityTest {
@Test
fun moreIssuesCard_twoIssuesAlreadyShown_expandAdditionalIssueCards() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
- safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingIssue)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
val bundle = Bundle()
bundle.putString(EXTRA_SAFETY_SOURCE_ID, SOURCE_ID_2)
bundle.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, RECOMMENDATION_ISSUE_ID)
context.launchSafetyCenterActivity(bundle) {
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
waitFindObject(By.text("See all alerts"))
assertSourceIssueNotDisplayed(safetySourceCtsData.informationIssue)
@@ -520,8 +657,8 @@ class SafetyCenterActivityTest {
// Verify cards expanded
waitTextNotDisplayed("See all alerts")
- assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingIssue)
- assertSourceIssueDisplayed(safetySourceCtsData.recommendationIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
+ assertSourceIssueDisplayed(safetySourceCtsData.recommendationGeneralIssue)
assertSourceIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagedDeviceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagedDeviceTest.kt
index 42872bf56..56cb44b43 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagedDeviceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagedDeviceTest.kt
@@ -252,9 +252,10 @@ class SafetyCenterManagedDeviceTest {
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun setSafetySourceData_issuesOnlySourceWithWorkProfile_shouldBeAbleToSetData() {
val managedSafetyCenterManager = getManagedSafetyCenterManager()
- val dataToSet = SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationIssue)
+ val dataToSet =
+ SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)
val dataToSetForWork =
- SafetySourceCtsData.issuesOnly(safetySourceCtsData.criticalResolvingIssue)
+ SafetySourceCtsData.issuesOnly(safetySourceCtsData.criticalResolvingGeneralIssue)
safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_ALL_PROFILE_CONFIG)
safetyCenterCtsHelper.setData(ISSUE_ONLY_ALL_PROFILE_SOURCE_ID, dataToSet)
managedSafetyCenterManager.setSafetySourceDataWithPermissionForManagedUser(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
index 175e9b9bf..ac92a0bed 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
@@ -57,6 +57,7 @@ import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
import android.safetycenter.SafetySourceErrorDetails
+import android.safetycenter.SafetySourceIssue
import android.safetycenter.cts.testing.Coroutines.TIMEOUT_LONG
import android.safetycenter.cts.testing.Coroutines.TIMEOUT_SHORT
import android.safetycenter.cts.testing.FakeExecutor
@@ -196,10 +197,10 @@ class SafetyCenterManagerTest {
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
.build()
- private val safetyCenterStatusRecommendationOneAlert =
+ private val safetyCenterStatusGeneralRecommendationOneAlert =
SafetyCenterStatus.Builder(
safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_recommendation_title"),
+ "overall_severity_level_safety_recommendation_title"),
getAlertString(1))
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.build()
@@ -212,18 +213,26 @@ class SafetyCenterManagerTest {
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
.build()
- private val safetyCenterStatusCriticalOneAlert =
+ private val safetyCenterStatusDeviceRecommendationOneAlert =
SafetyCenterStatus.Builder(
safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_warning_title"),
+ "overall_severity_level_device_recommendation_title"),
+ getAlertString(1))
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ private val safetyCenterStatusGeneralCriticalOneAlert =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesContext.getStringByName(
+ "overall_severity_level_critical_safety_warning_title"),
getAlertString(1))
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
- private val safetyCenterStatusCriticalTwoAlerts =
+ private val safetyCenterStatusGeneralCriticalTwoAlerts =
SafetyCenterStatus.Builder(
safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_warning_title"),
+ "overall_severity_level_critical_safety_warning_title"),
getAlertString(2))
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -236,10 +245,34 @@ class SafetyCenterManagerTest {
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
+ private val safetyCenterStatusAccountCriticalTwoAlerts =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesContext.getStringByName(
+ "overall_severity_level_critical_account_warning_title"),
+ getAlertString(2))
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusDeviceCriticalOneAlert =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesContext.getStringByName(
+ "overall_severity_level_critical_device_warning_title"),
+ getAlertString(1))
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ private val safetyCenterStatusDeviceCriticalTwoAlerts =
+ SafetyCenterStatus.Builder(
+ safetyCenterResourcesContext.getStringByName(
+ "overall_severity_level_critical_device_warning_title"),
+ getAlertString(2))
+ .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
private val safetyCenterStatusCriticalSixAlerts =
SafetyCenterStatus.Builder(
safetyCenterResourcesContext.getStringByName(
- "overall_severity_level_critical_warning_title"),
+ "overall_severity_level_critical_safety_warning_title"),
getAlertString(6))
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
.build()
@@ -385,9 +418,9 @@ class SafetyCenterManagerTest {
listOf(safetyCenterEntryOrGroupCritical),
emptyList())
- private val safetyCenterDataRecommendationOneAlert =
+ private val safetyCenterDataGeneralRecommendationOneAlert =
SafetyCenterData(
- safetyCenterStatusRecommendationOneAlert,
+ safetyCenterStatusGeneralRecommendationOneAlert,
listOf(safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
listOf(SafetyCenterEntryOrGroup(safetyCenterEntryRecommendation(SINGLE_SOURCE_ID))),
emptyList())
@@ -399,9 +432,16 @@ class SafetyCenterManagerTest {
listOf(SafetyCenterEntryOrGroup(safetyCenterEntryRecommendation(SINGLE_SOURCE_ID))),
emptyList())
- private val safetyCenterDataCriticalOneAlert =
+ private val safetyCenterDataDeviceRecommendationOneAlert =
SafetyCenterData(
- safetyCenterStatusCriticalOneAlert,
+ safetyCenterStatusDeviceRecommendationOneAlert,
+ listOf(safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
+ listOf(SafetyCenterEntryOrGroup(safetyCenterEntryRecommendation(SINGLE_SOURCE_ID))),
+ emptyList())
+
+ private val safetyCenterDataGeneralCriticalOneAlert =
+ SafetyCenterData(
+ safetyCenterStatusGeneralCriticalOneAlert,
listOf(safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
emptyList())
@@ -413,18 +453,16 @@ class SafetyCenterManagerTest {
listOf(safetyCenterEntryOrGroupCritical),
emptyList())
- private val safetyCenterDataCriticalTwoAlerts =
+ private val safetyCenterDataDeviceCriticalOneAlert =
SafetyCenterData(
- safetyCenterStatusCriticalTwoAlerts,
- listOf(
- safetyCenterIssueCritical(SINGLE_SOURCE_ID),
- safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)),
+ safetyCenterStatusDeviceCriticalOneAlert,
+ listOf(safetyCenterIssueCritical(SINGLE_SOURCE_ID)),
listOf(safetyCenterEntryOrGroupCritical),
emptyList())
private val safetyCenterDataCriticalOneAlertInFlight =
SafetyCenterData(
- safetyCenterStatusCriticalOneAlert,
+ safetyCenterStatusGeneralCriticalOneAlert,
listOf(safetyCenterIssueCritical(SINGLE_SOURCE_ID, isActionInFlight = true)),
listOf(safetyCenterEntryOrGroupCritical),
emptyList())
@@ -561,7 +599,7 @@ class SafetyCenterManagerTest {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified)
- val dataToSet = safetySourceCtsData.criticalWithResolvingIssue
+ val dataToSet = safetySourceCtsData.criticalWithResolvingGeneralIssue
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, dataToSet)
val apiSafetySourceData =
@@ -770,7 +808,7 @@ class SafetyCenterManagerTest {
val thrown =
assertFailsWith(IllegalArgumentException::class) {
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
}
assertThat(thrown)
@@ -785,7 +823,7 @@ class SafetyCenterManagerTest {
fun setSafetySourceData_withMaxSevRecommendation_met() {
safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG)
- val dataToSet = safetySourceCtsData.recommendationWithIssue
+ val dataToSet = safetySourceCtsData.recommendationWithGeneralIssue
safetyCenterCtsHelper.setData(DYNAMIC_ALL_OPTIONAL_ID, dataToSet)
val apiSafetySourceData =
@@ -800,7 +838,7 @@ class SafetyCenterManagerTest {
val thrown =
assertFailsWith(IllegalArgumentException::class) {
safetyCenterCtsHelper.setData(
- DYNAMIC_ALL_OPTIONAL_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ DYNAMIC_ALL_OPTIONAL_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
}
assertThat(thrown)
@@ -815,7 +853,8 @@ class SafetyCenterManagerTest {
fun setSafetySourceData_issueOnlyWithMaxSevRecommendation_met() {
safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG)
- val dataToSet = SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationIssue)
+ val dataToSet =
+ SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)
safetyCenterCtsHelper.setData(ISSUE_ONLY_ALL_OPTIONAL_ID, dataToSet)
val apiSafetySourceData =
@@ -831,7 +870,8 @@ class SafetyCenterManagerTest {
assertFailsWith(IllegalArgumentException::class) {
safetyCenterCtsHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceCtsData.issuesOnly(safetySourceCtsData.criticalResolvingIssue))
+ SafetySourceCtsData.issuesOnly(
+ safetySourceCtsData.criticalResolvingGeneralIssue))
}
assertThat(thrown)
@@ -1087,22 +1127,22 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withRefreshReasonRescanButtonClick_sourceSendsRescanData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
- safetySourceCtsData.criticalWithResolvingIssue
-
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.criticalWithResolvingGeneralIssue
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
- assertThat(apiSafetySourceData).isEqualTo(safetySourceCtsData.criticalWithResolvingIssue)
+ assertThat(apiSafetySourceData)
+ .isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue)
}
@Test
fun refreshSafetySources_withRefreshReasonPageOpen_sourceSendsPageOpenData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1117,7 +1157,7 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withRefreshReasonPageOpen_notCalledIfSourceDoesntSupportPageOpen() {
safetyCenterCtsHelper.setConfig(NO_PAGE_OPEN_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
assertFailsWith(TimeoutCancellationException::class) {
@@ -1135,7 +1175,7 @@ class SafetyCenterManagerTest {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] = null
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] = null
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK)
@@ -1149,10 +1189,10 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withMultipleSourcesInConfig_multipleSourcesSendData() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_1)] =
- safetySourceCtsData.criticalWithResolvingIssue
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_1)] =
+ safetySourceCtsData.criticalWithResolvingGeneralIssue
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_3)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_3)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1160,7 +1200,8 @@ class SafetyCenterManagerTest {
val apiSafetySourceData1 =
safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_1)
- assertThat(apiSafetySourceData1).isEqualTo(safetySourceCtsData.criticalWithResolvingIssue)
+ assertThat(apiSafetySourceData1)
+ .isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue)
val apiSafetySourceData2 =
safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_2)
assertThat(apiSafetySourceData2).isNull()
@@ -1173,7 +1214,7 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withMultipleSourcesOnPageOpen_onlyUpdatesAllowedSources() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
SafetySourceReceiver.safetySourceData[SafetySourceDataKey(REFRESH_GET_DATA, SOURCE_ID_1)] =
- safetySourceCtsData.criticalWithResolvingIssue
+ safetySourceCtsData.criticalWithResolvingGeneralIssue
SafetySourceReceiver.safetySourceData[SafetySourceDataKey(REFRESH_GET_DATA, SOURCE_ID_3)] =
safetySourceCtsData.information
@@ -1182,7 +1223,8 @@ class SafetyCenterManagerTest {
val apiSafetySourceData1 =
safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_1)
- assertThat(apiSafetySourceData1).isEqualTo(safetySourceCtsData.criticalWithResolvingIssue)
+ assertThat(apiSafetySourceData1)
+ .isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue)
val apiSafetySourceData2 =
safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_2)
assertThat(apiSafetySourceData2).isNull()
@@ -1196,8 +1238,8 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_whenReceiverDoesntHavePermission_sourceDoesntSendData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
- safetySourceCtsData.criticalWithResolvingIssue
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.criticalWithResolvingGeneralIssue
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait(
@@ -1211,7 +1253,7 @@ class SafetyCenterManagerTest {
@Test
fun refreshSafetySources_whenSourceNotInConfig_sourceDoesntSendData() {
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
assertFailsWith(TimeoutCancellationException::class) {
@@ -1235,7 +1277,7 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_sendsDifferentBroadcastIdsOnEachMethodCall() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
val broadcastId1 =
@@ -1253,7 +1295,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.refreshTimeout = TIMEOUT_SHORT
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
SafetySourceReceiver.overrideBroadcastId = "invalid"
val listener = safetyCenterCtsHelper.addListener()
@@ -1277,27 +1319,28 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_refreshAfterSuccessfulRefresh_completesSuccessfully() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
- safetySourceCtsData.criticalWithResolvingIssue
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.criticalWithResolvingGeneralIssue
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
- assertThat(apiSafetySourceData).isEqualTo(safetySourceCtsData.criticalWithResolvingIssue)
+ assertThat(apiSafetySourceData)
+ .isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue)
}
@Test
fun refreshSafetySources_refreshAfterFailedRefresh_completesSuccessfully() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
SafetySourceReceiver.shouldReportSafetySourceError = true
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1327,7 +1370,7 @@ class SafetyCenterManagerTest {
listener.receiveSafetyCenterErrorDetails()
SafetyCenterFlags.refreshTimeout = TIMEOUT_LONG
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1343,7 +1386,7 @@ class SafetyCenterManagerTest {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
@@ -1361,7 +1404,7 @@ class SafetyCenterManagerTest {
// SOURCE_ID_1 will timeout
for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) {
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, sourceId)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, sourceId)] =
safetySourceCtsData.information
}
val listener = safetyCenterCtsHelper.addListener()
@@ -1384,7 +1427,7 @@ class SafetyCenterManagerTest {
// SOURCE_ID_1 will timeout
for (sourceId in listOf(SOURCE_ID_2, SOURCE_ID_3)) {
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, sourceId)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, sourceId)] =
safetySourceCtsData.information
}
val listener = safetyCenterCtsHelper.addListener()
@@ -1404,7 +1447,7 @@ class SafetyCenterManagerTest {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
// SOURCE_ID_1 and SOURCE_ID_2 will timeout
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_3)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SOURCE_ID_3)] =
safetySourceCtsData.information
val listener = safetyCenterCtsHelper.addListener()
@@ -1478,7 +1521,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.refreshTimeout = TIMEOUT_LONG
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK)
@@ -1493,7 +1536,7 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withRefreshReasonRescanButtonClick_notifiesUiDuringRescan() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_FETCH_FRESH_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
val listener = safetyCenterCtsHelper.addListener()
@@ -1515,7 +1558,7 @@ class SafetyCenterManagerTest {
fun refreshSafetySources_withRefreshReasonPageOpen_notifiesUiWithFetch() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
+ SafetySourceDataKey(REFRESH_GET_DATA, SINGLE_SOURCE_ID)] =
safetySourceCtsData.information
val listener = safetyCenterCtsHelper.addListener()
@@ -1627,12 +1670,12 @@ class SafetyCenterManagerTest {
val previousApiSafetyCenterData =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
assertThat(apiSafetyCenterData).isNotEqualTo(previousApiSafetyCenterData)
- assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataCriticalOneAlert)
+ assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
}
@Test
@@ -1657,17 +1700,18 @@ class SafetyCenterManagerTest {
}
@Test
- fun getSafetyCenterData_withRecommendationIssue_returnsOverallStatusRecommendationOneAlert() {
+ fun getSafetyCenterData_withRecommendationGeneralIssue_returnsGeneralRecommendationOneAlert() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
- safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithIssue)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithGeneralIssue)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
- assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataRecommendationOneAlert)
+ assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataGeneralRecommendationOneAlert)
}
@Test
- fun getSafetyCenterData_withAccountIssue_returnsOverallStatusAccountRecommendationOneAlert() {
+ fun getSafetyCenterData_withRecommendationAccountIssue_returnsAccountRecommendationOneAlert() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithAccountIssue)
@@ -1678,7 +1722,29 @@ class SafetyCenterManagerTest {
}
@Test
- fun getSafetyCenterData_withAccountIssue_returnsOverallStatusAccountCriticalOneAlert() {
+ fun getSafetyCenterData_withRecommendationDeviceIssue_returnsDeviceRecommendationOneAlert() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithDeviceIssue)
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataDeviceRecommendationOneAlert)
+ }
+
+ @Test
+ fun getSafetyCenterData_withCriticalGeneralIssue_returnsOverallStatusGeneralCriticalOneAlert() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
+ }
+
+ @Test
+ fun getSafetyCenterData_withCriticalAccountIssue_returnsOverallStatusAccountCriticalOneAlert() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingAccountIssue)
@@ -1689,31 +1755,94 @@ class SafetyCenterManagerTest {
}
@Test
- fun getSafetyCenterData_withAccountAndOtherIssue_returnsOverallStatusCriticalTwoAlerts() {
+ fun getSafetyCenterData_withCriticalDeviceIssue_returnsOverallStatusDeviceCriticalOneAlert() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
- safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithTwoIssues)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingDeviceIssue)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
- assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataCriticalTwoAlerts)
+ assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataDeviceCriticalOneAlert)
+ }
+
+ @Test
+ fun getSafetyCenterData_singleSourceIssues_returnsOverallStatusBasedOnHigherSeverityIssue() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID,
+ safetySourceCtsData.criticalWithResolvingDeviceIssueAndRecommendationIssue)
+
+ val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+ assertThat(apiSafetyCenterStatus).isEqualTo(safetyCenterStatusDeviceCriticalTwoAlerts)
+ }
+
+ @Test
+ fun getSafetyCenterData_multipleSourcesIssues_returnsOverallStatusBasedOnHigherSeverityIssue() {
+ safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.recommendationWithAccountIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_3, safetySourceCtsData.criticalWithResolvingDeviceIssue)
+
+ val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+ assertThat(apiSafetyCenterStatus).isEqualTo(safetyCenterStatusDeviceCriticalTwoAlerts)
+ }
+
+ @Test
+ fun getSafetyCenterData_multipleIssues_returnsOverallStatusBasedOnConfigOrdering() {
+ safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_1, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_3, safetySourceCtsData.criticalWithResolvingDeviceIssue)
+
+ val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+ assertThat(apiSafetyCenterStatus).isEqualTo(safetyCenterStatusGeneralCriticalTwoAlerts)
+ }
+
+ @Test
+ fun getSafetyCenterData_criticalDeviceIssues_returnsOverallStatusBasedOnAddIssueCallOrder() {
+ safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+ safetyCenterCtsHelper.setData(
+ SINGLE_SOURCE_ID,
+ safetySourceCtsData
+ .defaultCriticalDataBuilder()
+ .addIssue(
+ safetySourceCtsData
+ .defaultCriticalResolvingIssueBuilder("critical issue num 1")
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT)
+ .build())
+ .addIssue(
+ safetySourceCtsData
+ .defaultCriticalResolvingIssueBuilder("critical issue num 2")
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .build())
+ .build())
+
+ val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+ assertThat(apiSafetyCenterStatus).isEqualTo(safetyCenterStatusAccountCriticalTwoAlerts)
}
@Test
fun getSafetyCenterData_withComplexConfigWithSomeDataProvided_returnsDataProvided() {
safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG)
safetyCenterCtsHelper.setData(
- DYNAMIC_BAREBONE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ DYNAMIC_BAREBONE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterCtsHelper.setData(
- DYNAMIC_DISABLED_ID, safetySourceCtsData.recommendationWithIssue)
+ DYNAMIC_DISABLED_ID, safetySourceCtsData.recommendationWithGeneralIssue)
safetyCenterCtsHelper.setData(DYNAMIC_HIDDEN_ID, safetySourceCtsData.unspecified)
safetyCenterCtsHelper.setData(
DYNAMIC_HIDDEN_WITH_SEARCH_ID, safetySourceCtsData.information)
safetyCenterCtsHelper.setData(
ISSUE_ONLY_BAREBONE_ID,
- SafetySourceCtsData.issuesOnly(safetySourceCtsData.criticalResolvingIssue))
+ SafetySourceCtsData.issuesOnly(safetySourceCtsData.criticalResolvingGeneralIssue))
safetyCenterCtsHelper.setData(
ISSUE_ONLY_ALL_OPTIONAL_ID,
- SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationIssue))
+ SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue))
safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssue)
safetyCenterCtsHelper.setData(
ISSUE_ONLY_IN_RIGID_ID,
@@ -2069,10 +2198,10 @@ class SafetyCenterManagerTest {
listener.receiveSafetyCenterData()
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
- assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataCriticalOneAlert)
+ assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
}
@Test
@@ -2226,7 +2355,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_existing_callsListenerAndDismisses() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
@@ -2240,7 +2369,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_issueShowsUpAgainIfSourceStopsSendingItAtLeastOnce() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID))
val listener = safetyCenterCtsHelper.addListener()
@@ -2253,19 +2382,19 @@ class SafetyCenterManagerTest {
.isEqualTo(safetyCenterDataOk)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val safetyCenterDataAfterSourcePushesDismissedIssueAgain =
listener.receiveSafetyCenterData()
assertThat(safetyCenterDataAfterSourcePushesDismissedIssueAgain)
- .isEqualTo(safetyCenterDataCriticalOneAlert)
+ .isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
}
@Test
fun dismissSafetyCenterIssue_existingWithDifferentIssueType_callsListenerAndDismisses() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
@@ -2282,7 +2411,8 @@ class SafetyCenterManagerTest {
safetyCenterCtsHelper.setData(
SINGLE_SOURCE_ID, safetySourceCtsData.recommendationDismissPendingIntentIssue)
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(DISMISS_ISSUE, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(DISMISS_ISSUE, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.dismissSafetyCenterIssueWithPermissionAndWait(
@@ -2319,7 +2449,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_nonExisting_doesntCallListenerOrDismiss() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
@@ -2334,7 +2464,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_alreadyDismissed_doesntCallListenerOrDismiss() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID))
val listener = safetyCenterCtsHelper.addListener()
@@ -2351,7 +2481,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_dismissedWithDifferentIssueType_doesntCallListenerOrDismiss() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterManager.dismissSafetyCenterIssueWithPermission(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID))
val listener = safetyCenterCtsHelper.addListener()
@@ -2369,7 +2499,7 @@ class SafetyCenterManagerTest {
fun dismissSafetyCenterIssue_withFlagDisabled_doesntCallListenerOrDismiss() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterCtsHelper.setEnabled(false)
@@ -2408,10 +2538,11 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_existing_callsListenerWithInFlightActionAndExecutes() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2448,7 +2579,7 @@ class SafetyCenterManagerTest {
@Test
fun executeSafetyCenterIssueAction_existing_errorWithDispatchingResolvingAction() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
- val criticalWithResolvingIssue = safetySourceCtsData.criticalWithResolvingIssue
+ val criticalWithResolvingIssue = safetySourceCtsData.criticalWithResolvingGeneralIssue
criticalWithResolvingIssue.issues.first().actions.first().pendingIntent.cancel()
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, criticalWithResolvingIssue)
val listener = safetyCenterCtsHelper.addListener()
@@ -2495,11 +2626,12 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_existing_unmarkInFlightWhenInlineActionError() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
SafetySourceReceiver.shouldReportSafetySourceError = true
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2511,7 +2643,7 @@ class SafetyCenterManagerTest {
.isEqualTo(safetyCenterDataCriticalOneAlertInFlight)
val safetyCenterDataFromListenerAfterInlineAction = listener.receiveSafetyCenterData()
assertThat(safetyCenterDataFromListenerAfterInlineAction)
- .isEqualTo(safetyCenterDataCriticalOneAlert)
+ .isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
val error = listener.receiveSafetyCenterErrorDetails()
assertThat(error)
.isEqualTo(
@@ -2541,7 +2673,7 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_alreadyInFlight_doesntCallListenerOrExecute() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2566,7 +2698,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.resolveActionTimeout = TIMEOUT_SHORT
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -2579,7 +2711,7 @@ class SafetyCenterManagerTest {
.isEqualTo(safetyCenterDataCriticalOneAlertInFlight)
val safetyCenterDataFromListenerAfterInlineAction = listener.receiveSafetyCenterData()
assertThat(safetyCenterDataFromListenerAfterInlineAction)
- .isEqualTo(safetyCenterDataCriticalOneAlert)
+ .isEqualTo(safetyCenterDataGeneralCriticalOneAlert)
val error = listener.receiveSafetyCenterErrorDetails()
assertThat(error)
.isEqualTo(
@@ -2592,7 +2724,7 @@ class SafetyCenterManagerTest {
SafetyCenterFlags.resolveActionTimeout = TIMEOUT_SHORT
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2603,7 +2735,8 @@ class SafetyCenterManagerTest {
listener.receiveSafetyCenterErrorDetails()
SafetyCenterFlags.resolveActionTimeout = TIMEOUT_LONG
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2621,7 +2754,7 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_withFlagDisabled_doesntCallListenerOrExecute() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
safetyCenterCtsHelper.setEnabled(false)
@@ -2648,10 +2781,11 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_issueIdDoesNotMatch_throwsErrorAndDoesNotResolveIssue() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -2670,10 +2804,11 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_actionIdDoesNotMatch_doesNotResolveIssue() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
@@ -2692,10 +2827,11 @@ class SafetyCenterManagerTest {
fun executeSafetyCenterIssueAction_idsDontMatch_canStillResolve() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
val listener = safetyCenterCtsHelper.addListener()
SafetySourceReceiver.safetySourceData[
- SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] = safetySourceCtsData.information
+ SafetySourceDataKey(RESOLVE_ACTION, SINGLE_SOURCE_ID)] =
+ safetySourceCtsData.information
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.executeSafetyCenterIssueActionWithPermissionAndWait(
SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID),
@@ -2750,7 +2886,8 @@ class SafetyCenterManagerTest {
fun clearAllSafetySourceDataForTests_clearsAllSafetySourceData() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.unspecified)
- safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.criticalWithResolvingIssue)
+ safetyCenterCtsHelper.setData(
+ SOURCE_ID_2, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
@@ -2764,7 +2901,7 @@ class SafetyCenterManagerTest {
fun clearAllSafetySourceDataForTests_withFlagDisabled_clearsData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(
- SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingIssue)
+ SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
safetyCenterCtsHelper.setEnabled(false)
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
@@ -2881,8 +3018,8 @@ class SafetyCenterManagerTest {
private fun SafetyCenterData.getGroupSummary(groupId: String): CharSequence =
entriesOrGroups
- .first { it.entryGroup?.id == SafetyCenterCtsData.entryGroupId(groupId) }
- .entryGroup!!
+ .first { it.entryGroup?.id == SafetyCenterCtsData.entryGroupId(groupId) }
+ .entryGroup!!
.summary!!
private fun safetyCenterEntryDefaultBuilder(sourceId: String) =
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
index cdbeaaa2f..91593fa71 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
@@ -130,7 +130,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(SINGLE_SOURCE_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
- safetySourceCtsData.criticalWithResolvingIssue,
+ safetySourceCtsData.criticalWithResolvingGeneralIssue,
EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceData =
@@ -291,7 +291,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(SINGLE_SOURCE_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
- safetySourceCtsData.criticalWithResolvingIssue,
+ safetySourceCtsData.criticalWithResolvingGeneralIssue,
EVENT_SOURCE_STATE_CHANGED)
val listener = SafetyCenterCtsListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
@@ -317,7 +317,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(SINGLE_SOURCE_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
- safetySourceCtsData.criticalWithResolvingIssue,
+ safetySourceCtsData.criticalWithResolvingGeneralIssue,
EVENT_SOURCE_STATE_CHANGED)
val listener = SafetyCenterCtsListener()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
@@ -347,7 +347,7 @@ class SafetyCenterUnsupportedTest {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(SINGLE_SOURCE_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
SINGLE_SOURCE_ID,
- safetySourceCtsData.criticalWithResolvingIssue,
+ safetySourceCtsData.criticalWithResolvingGeneralIssue,
EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceDataBeforeClearing =
safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsHelper.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsHelper.kt
index 2b917e3ac..1c9791164 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsHelper.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsHelper.kt
@@ -56,6 +56,7 @@ class SafetyCenterCtsHelper(private val context: Context) {
SafetyCenterFlags.untrackedSources = emptySet()
SafetyCenterFlags.resurfaceIssueMaxCounts = emptyMap()
SafetyCenterFlags.resurfaceIssueDelays = emptyMap()
+ SafetyCenterFlags.noBackgroundRefreshSources = emptySet()
setEnabled(true)
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
index 996c9744d..19cee1544 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
@@ -83,6 +83,13 @@ object SafetyCenterFlags {
"safety_center_resurface_issue_delays_millis"
/**
+ * Comma delimited list of IDs of sources that should only be refreshed when Safety Center is on
+ * screen. We will refresh these sources only on page open and when the scan button is clicked.
+ */
+ private const val PROPERTY_NO_BACKGROUND_REFRESH_SOURCES =
+ "safety_center_no_background_refresh_sources"
+
+ /**
* Default time for which a Safety Center refresh is allowed to wait for a source to respond to
* a refresh request before timing out and marking the refresh as finished.
*/
@@ -211,6 +218,19 @@ object SafetyCenterFlags {
Int::toString, { value -> value.toMillis().toString() }))
}
+ /**
+ * A property that allows getting and setting the [PROPERTY_NO_BACKGROUND_REFRESH_SOURCES]
+ * device config flag.
+ */
+ var noBackgroundRefreshSources: Set<String>
+ get() =
+ readFlag(PROPERTY_NO_BACKGROUND_REFRESH_SOURCES, defaultValue = emptySet()) {
+ it.split(",").toSet()
+ }
+ set(value) {
+ writeFlag(PROPERTY_NO_BACKGROUND_REFRESH_SOURCES, value.joinToString(","))
+ }
+
/** Converts a comma separated list of colon separated pairs into a map. */
private fun <K, V> String.toMap(
keyFromString: (String) -> K,
@@ -268,7 +288,8 @@ object SafetyCenterFlags {
PROPERTY_SAFETY_CENTER_RESOLVE_ACTION_TIMEOUT,
PROPERTY_UNTRACKED_SOURCES,
PROPERTY_RESURFACE_ISSUE_MAX_COUNTS,
- PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS)
+ PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS,
+ PROPERTY_NO_BACKGROUND_REFRESH_SOURCES)
},
READ_DEVICE_CONFIG)
}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
index 2d0515d9b..397373237 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
@@ -163,8 +163,11 @@ class SafetySourceCtsData(private val context: Context) {
.addIssue(informationIssueWithSubtitle)
.build()
- /** A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION] and a redirecting [Action]. */
- val recommendationIssue =
+ /**
+ * A [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_RECOMMENDATION] and a redirecting
+ * [Action].
+ */
+ private fun defaultRecommendationIssueBuilder() =
SafetySourceIssue.Builder(
RECOMMENDATION_ISSUE_ID,
"Recommendation issue title",
@@ -174,25 +177,31 @@ class SafetySourceCtsData(private val context: Context) {
.addAction(
Action.Builder(RECOMMENDATION_ISSUE_ACTION_ID, "See issue", redirectPendingIntent)
.build())
- .build()
/**
- * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION] and a redirecting [Action],
- * related to the account.
+ * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], general category and a
+ * redirecting [Action].
*/
- val accountRecommendationIssue =
- SafetySourceIssue.Builder(
- RECOMMENDATION_ISSUE_ID,
- "Recommendation issue title",
- "Recommendation issue summary",
- SEVERITY_LEVEL_RECOMMENDATION,
- ISSUE_TYPE_ID)
- .addAction(
- Action.Builder(RECOMMENDATION_ISSUE_ACTION_ID, "See issue", redirectPendingIntent)
- .build())
+ val recommendationGeneralIssue = defaultRecommendationIssueBuilder().build()
+
+ /**
+ * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], account category and a
+ * redirecting [Action].
+ */
+ val recommendationAccountIssue =
+ defaultRecommendationIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT)
.build()
+ /**
+ * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], device category and a
+ * redirecting [Action].
+ */
+ val recommendationDeviceIssue =
+ defaultRecommendationIssueBuilder()
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .build()
+
private val dismissIssuePendingIntent =
broadcastPendingIntent(
Intent(ACTION_HANDLE_DISMISSED_ISSUE).putExtra(EXTRA_SOURCE_ID, SINGLE_SOURCE_ID))
@@ -201,23 +210,12 @@ class SafetySourceCtsData(private val context: Context) {
* A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION] and a dismiss [PendingIntent].
*/
val recommendationIssueWithDismissPendingIntent =
- SafetySourceIssue.Builder(
- RECOMMENDATION_ISSUE_ID,
- "Recommendation issue title",
- "Recommendation issue summary",
- SEVERITY_LEVEL_RECOMMENDATION,
- ISSUE_TYPE_ID)
+ defaultRecommendationIssueBuilder()
.setOnDismissPendingIntent(dismissIssuePendingIntent)
- .addAction(
- Action.Builder(RECOMMENDATION_ISSUE_ACTION_ID, "See issue", redirectPendingIntent)
- .build())
.build()
- /**
- * A [SafetySourceData] with a [SEVERITY_LEVEL_RECOMMENDATION] redirecting [SafetySourceIssue]
- * and [SafetySourceStatus].
- */
- val recommendationWithIssue =
+ /** A [SafetySourceData.Builder] with a [SEVERITY_LEVEL_RECOMMENDATION] status. */
+ fun defaultRecommendationDataBuilder() =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(
@@ -226,42 +224,38 @@ class SafetySourceCtsData(private val context: Context) {
SEVERITY_LEVEL_RECOMMENDATION)
.setPendingIntent(redirectPendingIntent)
.build())
- .addIssue(recommendationIssue)
- .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_RECOMMENDATION] redirecting [SafetySourceIssue]
+ * and [SafetySourceStatus], only containing a general issue.
+ */
+ val recommendationWithGeneralIssue =
+ defaultRecommendationDataBuilder().addIssue(recommendationGeneralIssue).build()
/**
* A [SafetySourceData] with a [SEVERITY_LEVEL_RECOMMENDATION] redirecting [SafetySourceIssue]
* and [SafetySourceStatus], only containing an account issue.
*/
val recommendationWithAccountIssue =
- SafetySourceData.Builder()
- .setStatus(
- SafetySourceStatus.Builder(
- "Recommendation title",
- "Recommendation summary",
- SEVERITY_LEVEL_RECOMMENDATION)
- .setPendingIntent(redirectPendingIntent)
- .build())
- .addIssue(accountRecommendationIssue)
- .build()
+ defaultRecommendationDataBuilder().addIssue(recommendationAccountIssue).build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_RECOMMENDATION] redirecting [SafetySourceIssue]
+ * and [SafetySourceStatus], only containing a device issue.
+ */
+ val recommendationWithDeviceIssue =
+ defaultRecommendationDataBuilder().addIssue(recommendationDeviceIssue).build()
/**
* A [SafetySourceData] with a [SEVERITY_LEVEL_RECOMMENDATION] [SafetySourceIssue] that has a
* dismiss [PendingIntent], and [SafetySourceStatus].
*/
val recommendationDismissPendingIntentIssue =
- SafetySourceData.Builder()
- .setStatus(
- SafetySourceStatus.Builder(
- "Recommendation title",
- "Recommendation summary",
- SEVERITY_LEVEL_RECOMMENDATION)
- .setPendingIntent(redirectPendingIntent)
- .build())
+ defaultRecommendationDataBuilder()
.addIssue(recommendationIssueWithDismissPendingIntent)
.build()
- /** A [PendingIntent] used by the resolving [Action] in [criticalResolvingIssue]. */
+ /** A [PendingIntent] used by the resolving [Action] in [criticalResolvingGeneralIssue]. */
val criticalIssueActionPendingIntent =
broadcastPendingIntent(
Intent(ACTION_HANDLE_INLINE_ACTION)
@@ -269,19 +263,28 @@ class SafetySourceCtsData(private val context: Context) {
.putExtra(EXTRA_SOURCE_ISSUE_ID, CRITICAL_ISSUE_ID)
.putExtra(EXTRA_SOURCE_ISSUE_ACTION_ID, CRITICAL_ISSUE_ACTION_ID))
+ /** A resolving Critical [Action] */
+ val criticalResolvingAction =
+ Action.Builder(CRITICAL_ISSUE_ACTION_ID, "Solve issue", criticalIssueActionPendingIntent)
+ .setWillResolve(true)
+ .build()
+
+ /** A resolving Critical [Action] that declares a success message */
+ val criticalResolvingActionWithSuccessMessage =
+ Action.Builder(CRITICAL_ISSUE_ACTION_ID, "Solve issue", criticalIssueActionPendingIntent)
+ .setWillResolve(true)
+ .setSuccessMessage("Issue solved")
+ .build()
+
/** A [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]. */
- val criticalResolvingIssue =
+ val criticalResolvingIssueWithSuccessMessage =
SafetySourceIssue.Builder(
CRITICAL_ISSUE_ID,
"Critical issue title",
"Critical issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
ISSUE_TYPE_ID)
- .addAction(
- Action.Builder(
- CRITICAL_ISSUE_ACTION_ID, "Solve issue", criticalIssueActionPendingIntent)
- .setWillResolve(true)
- .build())
+ .addAction(criticalResolvingActionWithSuccessMessage)
.build()
/**
@@ -301,69 +304,104 @@ class SafetySourceCtsData(private val context: Context) {
.build()
/**
- * Account related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving
- * [Action].
+ * [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]
+ * .
*/
- val criticalResolvingAccountIssue =
+ fun defaultCriticalResolvingIssueBuilder(issueId: String = CRITICAL_ISSUE_ID) =
SafetySourceIssue.Builder(
- CRITICAL_ISSUE_ID,
+ issueId,
"Critical issue title",
"Critical issue summary",
SEVERITY_LEVEL_CRITICAL_WARNING,
ISSUE_TYPE_ID)
- .addAction(
- Action.Builder(
- CRITICAL_ISSUE_ACTION_ID, "Solve issue", criticalIssueActionPendingIntent)
- .setWillResolve(true)
- .build())
+ .addAction(criticalResolvingAction)
+
+ /**
+ * General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving [Action]
+ * .
+ */
+ val criticalResolvingGeneralIssue = defaultCriticalResolvingIssueBuilder().build()
+
+ /**
+ * Account related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving
+ * [Action].
+ */
+ val criticalResolvingAccountIssue =
+ defaultCriticalResolvingIssueBuilder()
.setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT)
.build()
/**
- * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving [SafetySourceIssue]
- * and [SafetySourceStatus].
+ * Device related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving
+ * [Action].
*/
- val criticalWithResolvingIssue =
+ val criticalResolvingDeviceIssue =
+ defaultCriticalResolvingIssueBuilder()
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .build()
+
+ /** A [SafetySourceData.Builder] with a [SEVERITY_LEVEL_CRITICAL_WARNING] status. */
+ fun defaultCriticalDataBuilder() =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(
"Critical title", "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING)
.setPendingIntent(redirectPendingIntent)
.build())
- .addIssue(criticalResolvingIssue)
- .build()
/**
- * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
- * [SEVERITY_LEVEL_CRITICAL_WARNING] [SafetySourceStatus].
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving general
+ * [SafetySourceIssue] and [SafetySourceStatus].
*/
- val criticalWithInformationIssue =
- SafetySourceData.Builder()
- .setStatus(
- SafetySourceStatus.Builder(
- "Critical title", "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING)
- .setPendingIntent(redirectPendingIntent)
- .build())
- .addIssue(informationIssue)
+ val criticalWithResolvingGeneralIssue =
+ defaultCriticalDataBuilder().addIssue(criticalResolvingGeneralIssue).build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving account related
+ * [SafetySourceIssue] and [SafetySourceStatus].
+ */
+ val criticalWithResolvingAccountIssue =
+ defaultCriticalDataBuilder().addIssue(criticalResolvingAccountIssue).build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving device related
+ * [SafetySourceIssue] and [SafetySourceStatus].
+ */
+ val criticalWithResolvingDeviceIssue =
+ defaultCriticalDataBuilder().addIssue(criticalResolvingDeviceIssue).build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving device related
+ * [SafetySourceIssue] and [SafetySourceStatus] and a recommendation issue.
+ */
+ val criticalWithResolvingDeviceIssueAndRecommendationIssue =
+ defaultCriticalDataBuilder()
+ .addIssue(criticalResolvingDeviceIssue)
+ .addIssue(recommendationAccountIssue)
.build()
/**
- * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a
- * [SEVERITY_LEVEL_RECOMMENDATION] [SafetySourceIssue]s and [SEVERITY_LEVEL_CRITICAL_WARNING]
- * [SafetySourceStatus]. One issue is account related, other isn't.
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving [SafetySourceIssue]
+ * and [SafetySourceStatus].
*/
- val criticalWithTwoIssues =
+ val criticalWithResolvingIssueWithSuccessMessage =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(
"Critical title", "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING)
.setPendingIntent(redirectPendingIntent)
.build())
- .addIssue(criticalResolvingAccountIssue)
- .addIssue(recommendationIssue)
+ .addIssue(criticalResolvingIssueWithSuccessMessage)
.build()
/**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
+ * [SEVERITY_LEVEL_CRITICAL_WARNING] [SafetySourceStatus].
+ */
+ val criticalWithInformationIssue =
+ defaultCriticalDataBuilder().addIssue(informationIssue).build()
+
+ /**
* Another [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] redirecting
* [SafetySourceIssue] and [SafetySourceStatus].
*/
@@ -378,20 +416,6 @@ class SafetySourceCtsData(private val context: Context) {
.build()
/**
- * Another [SafetySourceData] with a [SEVERITY_LEVEL_CRITICAL_WARNING] resolving account related
- * [SafetySourceIssue] and [SafetySourceStatus].
- */
- val criticalWithResolvingAccountIssue =
- SafetySourceData.Builder()
- .setStatus(
- SafetySourceStatus.Builder(
- "Critical title", "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING)
- .setPendingIntent(redirectPendingIntent)
- .build())
- .addIssue(criticalResolvingAccountIssue)
- .build()
-
- /**
* A function to generate simple [SafetySourceData] with the given entry [severityLevel] and
* [entrySummary], and an optional issue with the same [severityLevel].
*/
@@ -436,10 +460,10 @@ class SafetySourceCtsData(private val context: Context) {
/** Action ID for the redirecting action in [informationIssue]. */
const val INFORMATION_ISSUE_ACTION_ID = "information_issue_action_id"
- /** Issue ID for [recommendationIssue]. */
+ /** Issue ID for a recommendation issue */
const val RECOMMENDATION_ISSUE_ID = "recommendation_issue_id"
- /** Action ID for the redirecting action in [recommendationIssue]. */
+ /** Action ID for the redirecting action in recommendation issue. */
const val RECOMMENDATION_ISSUE_ACTION_ID = "recommendation_issue_action_id"
/** Issue ID for the critical issues in this file. */