diff options
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. */ |