Snap for 11828632 from 619af9349d to 24Q3-release

Change-Id: Id9c908477673ed3cd3c6a6bded87c9a7beef2f88
This commit is contained in:
Android Build Coastguard Worker
2024-05-11 01:21:20 +00:00
116 changed files with 2912 additions and 1150 deletions

View File

@@ -2551,9 +2551,9 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS" <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.accessibility.ColorContrastFragment" /> android:value="com.android.settings.display.ColorContrastFragment" />
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY" <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
android:value="@string/menu_key_accessibility"/> android:value="@string/menu_key_display"/>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" /> android:value="true" />
</activity> </activity>
@@ -3343,6 +3343,7 @@
<action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/> <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
<action android:name="com.android.settings.battery.action.PERIODIC_JOB_RECHECK"/> <action android:name="com.android.settings.battery.action.PERIODIC_JOB_RECHECK"/>
<action android:name="android.intent.action.TIME_SET"/> <action android:name="android.intent.action.TIME_SET"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
</intent-filter> </intent-filter>
</receiver> </receiver>
@@ -5138,8 +5139,6 @@
<action android:name="com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS" /> <action android:name="com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
android:value="@string/menu_key_security"/>
</activity> </activity>
<activity android:name=".privatespace.PrivateProfileContextHelperActivity" android:exported="false"/> <activity android:name=".privatespace.PrivateProfileContextHelperActivity" android:exported="false"/>

View File

@@ -45,6 +45,7 @@ message BatteryUsageHistoricalLogEntry {
FETCH_USAGE_DATA = 4; FETCH_USAGE_DATA = 4;
INSERT_USAGE_DATA = 5; INSERT_USAGE_DATA = 5;
TIME_UPDATED = 6; TIME_UPDATED = 6;
TIMEZONE_UPDATED = 7;
} }
optional int64 timestamp = 1; optional int64 timestamp = 1;

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M200,920Q167,920 143.5,896.5Q120,873 120,840L120,120Q120,87 143.5,63.5Q167,40 200,40L760,40Q793,40 816.5,63.5Q840,87 840,120L840,840Q840,873 816.5,896.5Q793,920 760,920L200,920ZM400,800L560,800L560,760L400,760L400,800ZM200,640L760,640L760,240L200,240L200,640Z"/>
</vector>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@*android:color/ripple_material_light">
<item
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorPrimaryContainer" />
<corners
android:radius="?android:attr/dialogCornerRadius" />
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask"
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@android:color/white" />
<corners
android:radius="?android:attr/dialogCornerRadius" />
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M240,800Q207,800 183.5,776.5Q160,753 160,720Q160,687 183.5,663.5Q207,640 240,640Q273,640 296.5,663.5Q320,687 320,720Q320,753 296.5,776.5Q273,800 240,800ZM480,800Q447,800 423.5,776.5Q400,753 400,720Q400,687 423.5,663.5Q447,640 480,640Q513,640 536.5,663.5Q560,687 560,720Q560,753 536.5,776.5Q513,800 480,800ZM720,800Q687,800 663.5,776.5Q640,753 640,720Q640,687 663.5,663.5Q687,640 720,640Q753,640 776.5,663.5Q800,687 800,720Q800,753 776.5,776.5Q753,800 720,800ZM240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560ZM240,320Q207,320 183.5,296.5Q160,273 160,240Q160,207 183.5,183.5Q207,160 240,160Q273,160 296.5,183.5Q320,207 320,240Q320,273 296.5,296.5Q273,320 240,320ZM480,320Q447,320 423.5,296.5Q400,273 400,240Q400,207 423.5,183.5Q447,160 480,160Q513,160 536.5,183.5Q560,207 560,240Q560,273 536.5,296.5Q513,320 480,320ZM720,320Q687,320 663.5,296.5Q640,273 640,240Q640,207 663.5,183.5Q687,160 720,160Q753,160 776.5,183.5Q800,207 800,240Q800,273 776.5,296.5Q753,320 720,320Z"/>
</vector>

View File

@@ -1,35 +0,0 @@
<!--
~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<com.android.settingslib.widget.AdaptiveIconShapeDrawable
android:width="@dimen/accessibility_icon_size"
android:height="@dimen/accessibility_icon_size"
android:color="@color/accessibility_feature_background"/>
</item>
<item android:gravity="center">
<vector
android:width="@dimen/accessibility_icon_foreground_size"
android:height="@dimen/accessibility_icon_foreground_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM11,19.93C7.06,19.44 4,16.08 4,12s3.05,-7.44 7,-7.93V19.93zM13,4.07C14.03,4.2 15,4.52 15.87,5H13V4.07zM13,7h5.24c0.25,0.31 0.48,0.65 0.68,1H13V7zM13,10h6.74c0.08,0.33 0.15,0.66 0.19,1H13V10zM13,19.93V19h2.87C15,19.48 14.03,19.8 13,19.93zM18.24,17H13v-1h5.92C18.72,16.35 18.49,16.69 18.24,17zM19.74,14H13v-1h6.93C19.89,13.34 19.82,13.67 19.74,14z"/>
</vector>
</item>
</layer-list>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M280,800L160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160L800,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L280,720L280,800ZM440,700Q465,700 482.5,682.5Q500,665 500,640Q500,615 482.5,597.5Q465,580 440,580Q415,580 397.5,597.5Q380,615 380,640Q380,665 397.5,682.5Q415,700 440,700ZM360,800L360,729Q341,712 330.5,689Q320,666 320,640Q320,614 330.5,591Q341,568 360,551L360,480L520,480L520,551Q539,568 549.5,591Q560,614 560,640Q560,666 549.5,689Q539,712 520,729L520,800L360,800ZM840,800L640,800Q623,800 611.5,788.5Q600,777 600,760L600,400Q600,383 611.5,371.5Q623,360 640,360L840,360Q857,360 868.5,371.5Q880,383 880,400L880,760Q880,777 868.5,788.5Q857,800 840,800Z"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M478,720Q499,720 513.5,705.5Q528,691 528,670Q528,649 513.5,634.5Q499,620 478,620Q457,620 442.5,634.5Q428,649 428,670Q428,691 442.5,705.5Q457,720 478,720ZM442,566L516,566Q516,533 523.5,514Q531,495 566,462Q592,436 607,412.5Q622,389 622,356Q622,300 581,270Q540,240 484,240Q427,240 391.5,270Q356,300 342,342L408,368Q413,350 430.5,329Q448,308 484,308Q516,308 532,325.5Q548,343 548,364Q548,384 536,401.5Q524,419 506,434Q462,473 452,493Q442,513 442,566ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M160,760L160,680L240,680L240,400Q240,317 290,252.5Q340,188 420,168L420,140Q420,115 437.5,97.5Q455,80 480,80Q505,80 522.5,97.5Q540,115 540,140L540,168Q620,188 670,252.5Q720,317 720,400L720,680L800,680L800,760L160,760ZM480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M440,660L440,440L520,440L520,660L440,660ZM480,360Q463,360 451.5,348.5Q440,337 440,320Q440,303 451.5,291.5Q463,280 480,280Q497,280 508.5,291.5Q520,303 520,320Q520,337 508.5,348.5Q497,360 480,360ZM280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,720L680,720L680,240L280,240L280,720Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,160Q447,160 423.5,136.5Q400,113 400,80Q400,47 423.5,23.5Q447,0 480,0Q513,0 536.5,23.5Q560,47 560,80Q560,113 536.5,136.5Q513,160 480,160ZM360,760L360,280Q300,275 238,265Q176,255 120,240L140,160Q218,181 306,190.5Q394,200 480,200Q566,200 654,190.5Q742,181 820,160L840,240Q784,255 722,265Q660,275 600,280L600,760L520,760L520,520L440,520L440,760L360,760ZM320,960Q303,960 291.5,948.5Q280,937 280,920Q280,903 291.5,891.5Q303,880 320,880Q337,880 348.5,891.5Q360,903 360,920Q360,937 348.5,948.5Q337,960 320,960ZM480,960Q463,960 451.5,948.5Q440,937 440,920Q440,903 451.5,891.5Q463,880 480,880Q497,880 508.5,891.5Q520,903 520,920Q520,937 508.5,948.5Q497,960 480,960ZM640,960Q623,960 611.5,948.5Q600,937 600,920Q600,903 611.5,891.5Q623,880 640,880Q657,880 668.5,891.5Q680,903 680,920Q680,937 668.5,948.5Q657,960 640,960Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M320,880Q303,880 291.5,868.5Q280,857 280,840L280,200Q280,183 291.5,171.5Q303,160 320,160L400,160L400,80L560,80L560,160L640,160Q657,160 668.5,171.5Q680,183 680,200L680,840Q680,857 668.5,868.5Q657,880 640,880L320,880Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,932L346,800L160,800L160,614L28,480L160,346L160,160L346,160L480,28L614,160L800,160L800,346L932,480L800,614L800,800L614,800L480,932ZM480,820L580,720L720,720L720,580L820,480L720,380L720,240L580,240L480,140L380,240L240,240L240,380L140,480L240,580L240,720L380,720L480,820ZM480,680Q563,680 621.5,621.5Q680,563 680,480Q680,397 621.5,338.5Q563,280 480,280L480,680Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M410,840L410,602L204,721L134,600L340,480L134,361L204,240L410,359L410,120L550,120L550,359L756,240L826,361L620,480L826,600L756,721L550,602L550,840L410,840Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,480Q513,480 536.5,456.5Q560,433 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,433 423.5,456.5Q447,480 480,480ZM480,880Q319,743 239.5,625.5Q160,508 160,408Q160,258 256.5,169Q353,80 480,80Q607,80 703.5,169Q800,258 800,408Q800,508 720.5,625.5Q641,743 480,880Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M280,600Q330,600 365,565Q400,530 400,480Q400,430 365,395Q330,360 280,360Q230,360 195,395Q160,430 160,480Q160,530 195,565Q230,600 280,600ZM280,720Q180,720 110,650Q40,580 40,480Q40,380 110,310Q180,240 280,240Q361,240 421.5,286Q482,332 506,400L841,400L920,479L780,639L680,560L600,640L520,560L506,560Q481,632 419,676Q357,720 280,720Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,568Q435,568 403.5,536.5Q372,505 372,460Q372,415 403.5,383.5Q435,352 480,352Q525,352 556.5,383.5Q588,415 588,460Q588,505 556.5,536.5Q525,568 480,568ZM480,760Q334,760 214,678.5Q94,597 40,460Q94,323 214,241.5Q334,160 480,160Q621,160 737.5,236Q854,312 912,440L760,440Q732,440 707,447Q682,454 660,467Q660,465 660,463.5Q660,462 660,460Q660,385 607.5,332.5Q555,280 480,280Q405,280 352.5,332.5Q300,385 300,460Q300,535 352.5,587.5Q405,640 480,640Q502,640 522.5,635Q543,630 561,621Q560,626 560,630.5Q560,635 560,640L560,753Q540,756 520,758Q500,760 480,760ZM680,840Q663,840 651.5,828.5Q640,817 640,800L640,680Q640,663 651.5,651.5Q663,640 680,640L680,640L680,600Q680,567 703.5,543.5Q727,520 760,520Q793,520 816.5,543.5Q840,567 840,600L840,640L840,640Q857,640 868.5,651.5Q880,663 880,680L880,800Q880,817 868.5,828.5Q857,840 840,840L680,840ZM720,640L800,640L800,600Q800,583 788.5,571.5Q777,560 760,560Q743,560 731.5,571.5Q720,583 720,600L720,640Z"/>
</vector>

View File

@@ -0,0 +1,29 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<group>
<clip-path
android:pathData="M4,2h16v20h-16z"/>
<path
android:pathData="M18.92,4.4L12.56,2.1C12.38,2.03 12.19,2 12,2C11.81,2 11.62,2.03 11.44,2.1L5.08,4.4C4.43,4.63 4,5.25 4,5.94V10.32C4.02,11.07 4.07,11.79 4.17,12.54C4.64,15.72 6.44,19.33 11.37,21.85C11.57,21.95 11.78,22 12,22C12.22,22 12.43,21.95 12.63,21.85C13.08,21.62 13.5,21.37 13.9,21.12C14.04,21.05 14.18,20.96 14.32,20.86C17.98,18.43 19.41,15.32 19.82,12.54C19.92,11.8 19.98,11.07 19.99,10.32V5.94C19.99,5.25 19.56,4.64 18.91,4.4H18.92ZM12.25,19.78C12.1,19.87 11.9,19.87 11.74,19.78C8.5,17.97 6.62,15.43 6.15,12.27C6.06,11.59 6.01,10.94 6,10.32V6.55C6,6.34 6.13,6.15 6.33,6.08L8.26,5.38C8.11,5.89 8.03,6.44 8.03,7.04C8.04,8.91 9.03,10.68 10.7,11.8C11.15,12.08 12.39,12.89 12.78,13.19C13.27,13.57 13.95,14.21 14.26,14.74C15.29,16.52 14.26,18.46 13.1,19.27C12.83,19.45 12.55,19.62 12.25,19.79V19.78ZM17.85,12.24C17.66,13.49 17.26,14.63 16.65,15.68C16.57,15.04 16.37,14.38 15.99,13.74C15.42,12.75 14.33,11.86 14.01,11.61C13.47,11.19 11.94,10.22 11.79,10.12C10.69,9.39 10.04,8.23 10.03,6.99C10.03,5.01 11.25,4.34 11.98,4.12C12.08,4.09 12.19,4.1 12.29,4.13L17.68,6.08C17.88,6.15 18.01,6.34 18.01,6.55V10.28C18,10.94 17.95,11.59 17.86,12.24H17.85Z"
android:fillColor="@android:color/white"/>
</group>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,400Q160,367 183.5,343.5Q207,320 240,320L280,320L280,240Q280,157 338.5,98.5Q397,40 480,40Q563,40 621.5,98.5Q680,157 680,240L680,320L720,320Q753,320 776.5,343.5Q800,367 800,400L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM480,680Q513,680 536.5,656.5Q560,633 560,600Q560,567 536.5,543.5Q513,520 480,520Q447,520 423.5,543.5Q400,567 400,600Q400,633 423.5,656.5Q447,680 480,680ZM360,320L600,320L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,320Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 112.5,324Q145,251 200.5,197Q256,143 330,111.5Q404,80 488,80Q568,80 639,107.5Q710,135 763.5,183.5Q817,232 848.5,298.5Q880,365 880,442Q880,557 810,618.5Q740,680 640,680L566,680Q557,680 553.5,685Q550,690 550,696Q550,708 565,730.5Q580,753 580,782Q580,832 552.5,856Q525,880 480,880ZM260,520Q286,520 303,503Q320,486 320,460Q320,434 303,417Q286,400 260,400Q234,400 217,417Q200,434 200,460Q200,486 217,503Q234,520 260,520ZM380,360Q406,360 423,343Q440,326 440,300Q440,274 423,257Q406,240 380,240Q354,240 337,257Q320,274 320,300Q320,326 337,343Q354,360 380,360ZM580,360Q606,360 623,343Q640,326 640,300Q640,274 623,257Q606,240 580,240Q554,240 537,257Q520,274 520,300Q520,326 537,343Q554,360 580,360ZM700,520Q726,520 743,503Q760,486 760,460Q760,434 743,417Q726,400 700,400Q674,400 657,417Q640,434 640,460Q640,486 657,503Q674,520 700,520Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,840Q438,840 409,811Q380,782 380,740Q380,698 409,669Q438,640 480,640Q522,640 551,669Q580,698 580,740Q580,782 551,811Q522,840 480,840ZM254,614L170,528Q229,469 308.5,434.5Q388,400 480,400Q572,400 651.5,435Q731,470 790,530L706,614Q662,570 604,545Q546,520 480,520Q414,520 356,545Q298,570 254,614ZM84,444L0,360Q92,266 215,213Q338,160 480,160Q622,160 745,213Q868,266 960,360L876,444Q799,367 697.5,323.5Q596,280 480,280Q364,280 262.5,323.5Q161,367 84,444Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M120,800L120,640L840,640L840,800L120,800ZM200,760L280,760L280,680L200,680L200,760ZM120,320L120,160L840,160L840,320L120,320ZM200,280L280,280L280,200L200,200L200,280ZM120,560L120,400L840,400L840,560L120,560ZM200,520L280,520L280,440L200,440L200,520Z"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M560,829L560,747Q650,721 705,647Q760,573 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,606 762,703.5Q684,801 560,829ZM120,600L120,360L280,360L480,160L480,800L280,600L120,600ZM560,640L560,318Q607,340 633.5,384Q660,428 660,480Q660,531 633.5,574.5Q607,618 560,640Z"/>
</vector>

View File

@@ -39,6 +39,7 @@
android:id="@+id/fingerprint_rename_field" android:id="@+id/fingerprint_rename_field"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textCapWords"/> android:inputType="textCapWords"
android:minHeight = "48dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:minHeight="72dp"
android:gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="40dp"
android:gravity="end|center_vertical"
android:orientation="horizontal"
android:paddingStart="24dp"
android:paddingEnd="8dp"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<androidx.preference.internal.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
app:maxWidth="40dp"
app:maxHeight="40dp"/>
</LinearLayout>
<RelativeLayout
android:id="@+id/text_frame"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingStart="@dimen/homepage_preference_text_padding_start"
android:paddingEnd="16dp">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:hyphenationFrequency="normalFast"
android:lineBreakWordStyle="phrase"
android:ellipsize="marquee"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
android:maxLines="4"
android:hyphenationFrequency="normalFast"
android:lineBreakWordStyle="phrase"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/SearchBarStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<Toolbar
android:id="@+id/search_action_bar_unified"
android:layout_width="match_parent"
android:layout_height="@dimen/search_bar_height"
android:paddingStart="8dp"
android:paddingEnd="24dp"
android:background="@drawable/search_bar_selected_background"
android:touchscreenBlocksFocus="false"
android:nextFocusForward="@+id/homepage_container"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
<TextView
android:id="@+id/search_bar_title"
style="@style/TextAppearance.SearchBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:layout_gravity="start"
android:text="@string/search_settings"/>
</Toolbar>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_bar_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:padding="6dp"
android:orientation="horizontal">
<include layout="@layout/search_bar_unified_version"/>
</LinearLayout>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/settings_homepage_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/main_content_scrollable_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.android.settings.widget.HomepageAppBarScrollingViewBehavior">
<LinearLayout
android:id="@+id/homepage_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/unified_suggestion_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" />
<FrameLayout
android:id="@+id/contextual_cards_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/contextual_card_side_margin"
android:layout_marginEnd="@dimen/contextual_card_side_margin"/>
<FrameLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:touchscreenBlocksFocus="false"
android:keyboardNavigationCluster="false">
<LinearLayout
android:id="@+id/app_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="76dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<include
android:id="@+id/homepage_app_bar_unified_view"
layout="@layout/settings_homepage_app_bar_unified_layout"/>
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -257,7 +257,7 @@
<bool name="config_show_device_header_in_device_info">true</bool> <bool name="config_show_device_header_in_device_info">true</bool>
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles --> <!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool> <bool name="config_force_rounded_icon_TopLevelSettings">false</bool>
<!-- Whether dismissal timestamp should be kept before deletion --> <!-- Whether dismissal timestamp should be kept before deletion -->
<bool name="config_keep_contextual_card_dismissal_timestamp">false</bool> <bool name="config_keep_contextual_card_dismissal_timestamp">false</bool>

View File

@@ -672,8 +672,10 @@
<string name="location_settings_footer_learn_more_content_description"> <string name="location_settings_footer_learn_more_content_description">
Learn more about Location settings Learn more about Location settings
</string> </string>
<!-- Tooltip for switchbar on Chrome devices. [CHAR LIMIT=90]--> <!-- Tooltip for switchbar on ChromeOS devices. [CHAR LIMIT=NONE]-->
<string name="location_settings_tooltip_text_for_chrome">To change location access, go to Settings > Security and Privacy > Privacy controls</string> <string name="location_settings_tooltip_text_for_chrome">
To change go to ChromeOS Settings > Privacy and security > Privacy controls > Location access
</string>
<!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] --> <!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
<string name="account_settings_title">Accounts</string> <string name="account_settings_title">Accounts</string>
@@ -4688,8 +4690,6 @@
<string name="accessibility_color_contrast_intro">Higher contrast makes text, buttons, and icons stand out more. Choose the contrast that looks best to you.</string> <string name="accessibility_color_contrast_intro">Higher contrast makes text, buttons, and icons stand out more. Choose the contrast that looks best to you.</string>
<!-- Notes in color contrast page footer for something should be aware. [CHAR LIMIT=NONE] --> <!-- Notes in color contrast page footer for something should be aware. [CHAR LIMIT=NONE] -->
<string name="color_contrast_note">Some apps may not support all color and text contrast settings</string> <string name="color_contrast_note">Some apps may not support all color and text contrast settings</string>
<!-- Summary for the accessibility color setting. [CHAR LIMIT=NONE] -->
<string name="accessibility_color_contrast_summary">Adjust how colors and text look against your screen\'s background color</string>
<!-- Preview screen title on the color contrast page. [CHAR LIMIT=20] --> <!-- Preview screen title on the color contrast page. [CHAR LIMIT=20] -->
<string name="color_contrast_preview">Preview</string> <string name="color_contrast_preview">Preview</string>
<!-- Preview screen email sender's name on the color contrast page. [CHAR LIMIT=15] --> <!-- Preview screen email sender's name on the color contrast page. [CHAR LIMIT=15] -->
@@ -12579,11 +12579,17 @@
<!-- Title for Thread network preference [CHAR_LIMIT=60] --> <!-- Title for Thread network preference [CHAR_LIMIT=60] -->
<string name="thread_network_settings_title">Thread</string> <string name="thread_network_settings_title">Thread</string>
<!-- Summary for Thread network preference. [CHAR_LIMIT=NONE]--> <!-- Title for Thread network settings main switch [CHAR_LIMIT=60] -->
<string name="thread_network_settings_summary">Connect to compatible devices using Thread for a seamless smart home experience</string> <string name="thread_network_settings_main_switch_title">Use Thread</string>
<!-- Summary for Thread network preference when airplane mode is enabled. [CHAR_LIMIT=NONE]--> <!-- Title for Thread network settings footer [CHAR_LIMIT=NONE] -->
<string name="thread_network_settings_summary_airplane_mode">Turn off airplane mode to use Thread</string> <string name="thread_network_settings_footer_title">Thread helps connect your smart home devices, boosting efficiency, and performance.\n\nWhen enabled, this device is eligible to join a Thread network, allowing control of Matter supported devices through this phone.</string>
<!-- Text for Thread network settings learn more link [CHAR_LIMIT=NONE] -->
<string name="thread_network_settings_learn_more">Learn more about Thread</string>
<!-- URL for Thread network settings learn more link [CHAR_LIMIT=NONE] -->
<string name="thread_network_settings_learn_more_link" translatable="false">https://developers.home.google.com</string>
<!-- Label for the camera use toggle [CHAR LIMIT=40] --> <!-- Label for the camera use toggle [CHAR LIMIT=40] -->
<string name="camera_toggle_title">Camera access</string> <string name="camera_toggle_title">Camera access</string>

View File

@@ -21,16 +21,6 @@
android:persistent="false" android:persistent="false"
android:title="@string/accessibility_color_and_motion_title"> android:title="@string/accessibility_color_and_motion_title">
<Preference
android:fragment="com.android.settings.accessibility.ColorContrastFragment"
android:key="color_contrast"
android:icon="@drawable/ic_color_contrast"
android:persistent="false"
android:title="@string/accessibility_color_contrast_title"
android:summary="@string/accessibility_color_contrast_summary"
settings:controller="com.android.settings.accessibility.ContrastPreferenceController"
settings:searchable="true"/>
<Preference <Preference
android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment" android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment"
android:icon="@drawable/ic_daltonizer" android:icon="@drawable/ic_daltonizer"

View File

@@ -28,7 +28,7 @@
android:key="color_contrast_selector" android:key="color_contrast_selector"
android:selectable="false" android:selectable="false"
android:layout="@layout/accessibility_color_contrast_selector" android:layout="@layout/accessibility_color_contrast_selector"
settings:controller="com.android.settings.accessibility.ContrastSelectorPreferenceController" /> settings:controller="com.android.settings.display.ContrastSelectorPreferenceController" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:key="toggle_high_text_contrast_preference" android:key="toggle_high_text_contrast_preference"
@@ -43,6 +43,6 @@
android:title="@string/color_contrast_note" android:title="@string/color_contrast_note"
android:selectable="false" android:selectable="false"
settings:searchable="false" settings:searchable="false"
settings:controller="com.android.settings.accessibility.ColorContrastFooterPreferenceController" /> settings:controller="com.android.settings.display.ColorContrastFooterPreferenceController" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -46,6 +46,17 @@
settings:controller="com.android.settings.wfd.WifiDisplayPreferenceController" settings:controller="com.android.settings.wfd.WifiDisplayPreferenceController"
settings:keywords="@string/keywords_wifi_display_settings" /> settings:keywords="@string/keywords_wifi_display_settings" />
<com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFragment"
android:key="thread_network_settings"
android:title="@string/thread_network_settings_title"
android:icon="@*android:drawable/ic_thread_network"
android:order="-5"
settings:searchable="false"
settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFragmentController"
settings:userRestriction="no_thread_network"
settings:useAdminDisabledSummary="true"/>
<com.android.settingslib.RestrictedPreference <com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.print.PrintSettingsFragment" android:fragment="com.android.settings.print.PrintSettingsFragment"
android:icon="@*android:drawable/ic_settings_print" android:icon="@*android:drawable/ic_settings_print"
@@ -63,15 +74,6 @@
settings:useAdminDisabledSummary="true" settings:useAdminDisabledSummary="true"
settings:userRestriction="no_ultra_wideband_radio" /> settings:userRestriction="no_ultra_wideband_radio" />
<com.android.settingslib.RestrictedSwitchPreference
android:key="thread_network_settings"
android:title="@string/thread_network_settings_title"
android:order="110"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController"
settings:userRestriction="no_thread_network"
settings:useAdminDisabledSummary="true"/>
<PreferenceCategory <PreferenceCategory
android:key="dashboard_tile_placeholder" android:key="dashboard_tile_placeholder"
android:order="-8" /> android:order="-8" />

View File

@@ -111,6 +111,14 @@
android:fragment="com.android.settings.display.ColorModePreferenceFragment" android:fragment="com.android.settings.display.ColorModePreferenceFragment"
settings:controller="com.android.settings.display.ColorModePreferenceController" settings:controller="com.android.settings.display.ColorModePreferenceController"
settings:keywords="@string/keywords_color_mode"/> settings:keywords="@string/keywords_color_mode"/>
<Preference
android:fragment="com.android.settings.display.ColorContrastFragment"
android:key="color_contrast"
android:persistent="false"
android:title="@string/accessibility_color_contrast_title"
settings:controller="com.android.settings.display.ContrastPreferenceController"
settings:searchable="true"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/thread_network_settings_title">
<com.android.settingslib.widget.MainSwitchPreference
android:key="toggle_thread_network"
android:title="@string/thread_network_settings_main_switch_title"
settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkToggleController"/>
<com.android.settingslib.widget.FooterPreference
android:key="thread_network_settings_footer"
android:title="@string/thread_network_settings_footer_title"
android:selectable="false"
settings:searchable="false"
settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFooterController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,241 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="top_level_settings">
<PreferenceCategory
android:order="-140"
android:key="top_level_account_category">
</PreferenceCategory>
<PreferenceCategory
android:order="-130"
android:key="top_level_connectivity_category">
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.network.NetworkDashboardFragment"
android:icon="@drawable/ic_settings_wireless_filled"
android:key="top_level_network"
android:order="-20"
android:title="@string/network_dashboard_title"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_network"
settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
android:icon="@drawable/ic_devices_other_filled"
android:key="top_level_connected_devices"
android:order="-10"
android:title="@string/connected_devices_dashboard_title"
android:summary="@string/connected_devices_dashboard_default_summary"
settings:highlightableMenuKey="@string/menu_key_connected_devices"
settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:order="-120"
android:key="top_level_personalize_category">
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.applications.AppDashboardFragment"
android:icon="@drawable/ic_apps_filled"
android:key="top_level_apps"
android:order="-60"
android:title="@string/apps_dashboard_title"
android:summary="@string/app_and_notification_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_apps"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.notification.ConfigureNotificationSettings"
android:icon="@drawable/ic_notifications_filled"
android:key="top_level_notifications"
android:order="-50"
android:title="@string/configure_notification_settings"
android:summary="@string/notification_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_notifications"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.notification.SoundSettings"
android:icon="@drawable/ic_volume_up_filled"
android:key="top_level_sound"
android:order="-40"
android:title="@string/sound_settings"
android:summary="@string/sound_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_sound"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.communal.CommunalDashboardFragment"
android:icon="@drawable/ia_settings_communal"
android:key="top_level_communal"
android:order="-30"
android:title="@string/communal_settings_title"
android:summary="@string/communal_settings_summary"
settings:highlightableMenuKey="@string/menu_key_communal"
settings:controller="com.android.settings.communal.CommunalPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.DisplaySettings"
android:icon="@drawable/ic_settings_display_filled"
android:key="top_level_display"
android:order="-20"
android:title="@string/display_settings"
android:summary="@string/display_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_display"
settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
<com.android.settings.widget.RestrictedHomepagePreference
android:icon="@drawable/ic_settings_wallpaper_filled"
android:key="top_level_wallpaper"
android:order="-10"
android:title="@string/wallpaper_settings_title"
android:summary="@string/wallpaper_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_wallpaper"
settings:controller="com.android.settings.display.TopLevelWallpaperPreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:order="-110"
android:key="top_level_system_info_category">
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
android:icon="@drawable/ic_storage_filled"
android:key="top_level_storage"
android:order="-50"
android:title="@string/storage_settings"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_storage"
settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.fuelgauge.batteryusage.PowerUsageSummary"
android:icon="@drawable/ic_settings_battery_filled"
android:key="top_level_battery"
android:order="-30"
android:title="@string/power_usage_summary_title"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_battery"
settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.system.SystemDashboardFragment"
android:icon="@drawable/ic_settings_system_dashboard_filled"
android:key="top_level_system"
android:order="-20"
android:title="@string/header_category_system"
android:summary="@string/system_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_system"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
android:icon="@drawable/ic_settings_about_device_filled"
android:key="top_level_about_device"
android:order="-10"
android:title="@string/about_settings"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_about_device"
settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:order="-100"
android:key="top_level_security_privacy_category">
<com.android.settings.widget.HomepagePreference
android:icon="@drawable/ic_settings_safety_center_filled"
android:key="top_level_safety_center"
android:order="-50"
android:title="@string/safety_center_title"
android:summary="@string/safety_center_summary"
settings:highlightableMenuKey="@string/menu_key_safety_center"
settings:controller="com.android.settings.safetycenter.TopLevelSafetyCenterEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.security.SecuritySettings"
android:icon="@drawable/ic_settings_security_filled"
android:key="top_level_security"
android:order="-40"
android:title="@string/security_settings_title"
android:summary="@string/security_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_security"
settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"
android:icon="@drawable/ic_settings_privacy_filled"
android:key="top_level_privacy"
android:order="-30"
android:title="@string/privacy_dashboard_title"
android:summary="@string/privacy_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_privacy"
settings:controller="com.android.settings.privacy.TopLevelPrivacyEntryPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.location.LocationSettings"
android:icon="@drawable/ic_settings_location_filled"
android:key="top_level_location"
android:order="-20"
android:title="@string/location_settings_title"
android:summary="@string/location_settings_loading_app_permission_stats"
settings:highlightableMenuKey="@string/menu_key_location"
settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.accounts.AccountDashboardFragment"
android:icon="@drawable/ic_settings_passwords_filled"
android:key="top_level_accounts"
android:order="-10"
android:title="@string/account_dashboard_title_with_passkeys"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_accounts"
settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:order="100"
android:key="top_level_support_category">
<com.android.settings.widget.HomepagePreference
android:key="top_level_emergency"
android:title="@string/emergency_settings_preference_title"
android:summary="@string/emergency_dashboard_summary"
android:icon="@drawable/ic_settings_emergency_filled"
android:order="-30"
android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"
settings:isPreferenceVisible="@bool/config_show_emergency_settings"
settings:highlightableMenuKey="@string/menu_key_emergency"/>
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.accessibility.AccessibilitySettings"
android:icon="@drawable/ic_settings_accessibility_filled"
android:key="top_level_accessibility"
android:order="-20"
android:title="@string/accessibility_settings"
android:summary="@string/accessibility_settings_summary"
settings:highlightableMenuKey="@string/menu_key_accessibility"
settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
<com.android.settings.widget.HomepagePreference
android:icon="@drawable/ic_help_filled"
android:key="top_level_support"
android:order="-10"
android:title="@string/page_tab_title_support"
android:summary="@string/support_summary"
settings:highlightableMenuKey="@string/menu_key_support"
settings:controller="com.android.settings.support.SupportPreferenceController"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.core.BasePreferenceController;
/**
* Controller for {@link ColorContrastFragment}.
*/
public class ContrastPreferenceController extends BasePreferenceController {
public ContrastPreferenceController(@NonNull Context context, @NonNull String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
// Hide color contrast entry point inside Accessibility settings.
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.net.thread.ThreadNetworkController
import android.net.thread.ThreadNetworkController.StateCallback
import android.net.thread.ThreadNetworkException
import android.os.OutcomeReceiver
import androidx.annotation.VisibleForTesting
import java.util.concurrent.Executor
/**
* A testable interface for [ThreadNetworkController] which is `final`.
*
* We are in a awkward situation that Android API guideline suggest `final` for API classes
* while Robolectric test is being deprecated for platform testing (See
* tests/robotests/new_tests_hook.sh). This force us to use "mockito-target-extended" but it's
* conflicting with the default "mockito-target" which is somehow indirectly depended by the
* `SettingsUnitTests` target.
*/
@VisibleForTesting
interface BaseThreadNetworkController {
fun setEnabled(
enabled: Boolean,
executor: Executor,
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
)
fun registerStateCallback(executor: Executor, callback: StateCallback)
fun unregisterStateCallback(callback: StateCallback)
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.content.Context
import android.util.Log
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settingslib.HelpUtils
import com.android.settingslib.widget.FooterPreference
/**
* The footer preference controller for Thread settings in
* "Connected devices > Connection preferences > Thread".
*/
class ThreadNetworkFooterController(
context: Context,
preferenceKey: String
) : BasePreferenceController(context, preferenceKey) {
override fun getAvailabilityStatus(): Int {
// The thread_network_settings screen won't be displayed and it doesn't matter if this
// controller always return AVAILABLE
return AVAILABLE
}
override fun displayPreference(screen: PreferenceScreen) {
val footer: FooterPreference? = screen.findPreference(KEY_PREFERENCE_FOOTER)
if (footer != null) {
footer.setLearnMoreAction { _ -> openLocaleLearnMoreLink() }
footer.setLearnMoreText(mContext.getString(R.string.thread_network_settings_learn_more))
}
}
private fun openLocaleLearnMoreLink() {
val intent = HelpUtils.getHelpIntent(
mContext,
mContext.getString(R.string.thread_network_settings_learn_more_link),
mContext::class.java.name
)
if (intent != null) {
mContext.startActivity(intent)
} else {
Log.w(TAG, "HelpIntent is null")
}
}
companion object {
private const val TAG = "ThreadNetworkSettings"
private const val KEY_PREFERENCE_FOOTER = "thread_network_settings_footer"
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.app.settings.SettingsEnums
import com.android.settings.R
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.search.BaseSearchIndexProvider
import com.android.settingslib.search.SearchIndexable
/** The fragment for Thread settings in "Connected devices > Connection preferences > Thread". */
@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
class ThreadNetworkFragment : DashboardFragment() {
override fun getPreferenceScreenResId() = R.xml.thread_network_settings
override fun getLogTag() = "ThreadNetworkFragment"
override fun getMetricsCategory() = SettingsEnums.CONNECTED_DEVICE_PREFERENCES_THREAD
companion object {
/** For Search. */
@JvmField
val SEARCH_INDEX_DATA_PROVIDER = BaseSearchIndexProvider(R.xml.thread_network_settings)
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.content.Context
import android.net.thread.ThreadNetworkController
import android.net.thread.ThreadNetworkController.StateCallback
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.flags.Flags
import java.util.concurrent.Executor
/**
* The fragment controller for Thread settings in
* "Connected devices > Connection preferences > Thread".
*/
class ThreadNetworkFragmentController @VisibleForTesting constructor(
context: Context,
preferenceKey: String,
private val executor: Executor,
private val threadController: BaseThreadNetworkController?
) : BasePreferenceController(context, preferenceKey), LifecycleEventObserver {
private val stateCallback: StateCallback
private var threadEnabled = false
private var preference: Preference? = null
constructor(context: Context, preferenceKey: String) : this(
context,
preferenceKey,
ContextCompat.getMainExecutor(context),
ThreadNetworkUtils.getThreadNetworkController(context)
)
init {
stateCallback = newStateCallback()
}
override fun getAvailabilityStatus(): Int {
return if (!Flags.threadSettingsEnabled()) {
CONDITIONALLY_UNAVAILABLE
} else if (threadController == null) {
UNSUPPORTED_ON_DEVICE
} else {
AVAILABLE
}
}
override fun getSummary(): CharSequence {
return if (threadEnabled) {
mContext.getText(R.string.switch_on_text)
} else {
mContext.getText(R.string.switch_off_text)
}
}
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (threadController == null) {
return
}
when (event) {
Lifecycle.Event.ON_START ->
threadController.registerStateCallback(executor, stateCallback)
Lifecycle.Event.ON_STOP ->
threadController.unregisterStateCallback(stateCallback)
else -> {}
}
}
private fun newStateCallback(): StateCallback {
return object : StateCallback {
override fun onThreadEnableStateChanged(enabledState: Int) {
threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
preference?.let { preference -> refreshSummary(preference) }
}
override fun onDeviceRoleChanged(role: Int) {}
}
}
}

View File

@@ -1,236 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.thread.ThreadNetworkController
import android.net.thread.ThreadNetworkController.StateCallback
import android.net.thread.ThreadNetworkException
import android.net.thread.ThreadNetworkManager
import android.os.OutcomeReceiver
import android.provider.Settings
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.TogglePreferenceController
import com.android.settings.flags.Flags
import java.util.concurrent.Executor
/** Controller for the "Thread" toggle in "Connected devices > Connection preferences". */
class ThreadNetworkPreferenceController @VisibleForTesting constructor(
context: Context,
key: String,
private val executor: Executor,
private val threadController: BaseThreadNetworkController?
) : TogglePreferenceController(context, key), LifecycleEventObserver {
private val stateCallback: StateCallback
private val airplaneModeReceiver: BroadcastReceiver
private var threadEnabled = false
private var airplaneModeOn = false
private var preference: Preference? = null
/**
* A testable interface for [ThreadNetworkController] which is `final`.
*
* We are in a awkward situation that Android API guideline suggest `final` for API classes
* while Robolectric test is being deprecated for platform testing (See
* tests/robotests/new_tests_hook.sh). This force us to use "mockito-target-extended" but it's
* conflicting with the default "mockito-target" which is somehow indirectly depended by the
* `SettingsUnitTests` target.
*/
@VisibleForTesting
interface BaseThreadNetworkController {
fun setEnabled(
enabled: Boolean,
executor: Executor,
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
)
fun registerStateCallback(executor: Executor, callback: StateCallback)
fun unregisterStateCallback(callback: StateCallback)
}
constructor(context: Context, key: String) : this(
context,
key,
ContextCompat.getMainExecutor(context),
getThreadNetworkController(context)
)
init {
stateCallback = newStateCallback()
airplaneModeReceiver = newAirPlaneModeReceiver()
}
val isThreadSupportedOnDevice: Boolean
get() = threadController != null
private fun newStateCallback(): StateCallback {
return object : StateCallback {
override fun onThreadEnableStateChanged(enabledState: Int) {
threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
}
override fun onDeviceRoleChanged(role: Int) {}
}
}
private fun newAirPlaneModeReceiver(): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
airplaneModeOn = isAirplaneModeOn(context)
Log.i(TAG, "Airplane mode is " + if (airplaneModeOn) "ON" else "OFF")
preference?.let { preference -> updateState(preference) }
}
}
}
override fun getAvailabilityStatus(): Int {
return if (!Flags.threadSettingsEnabled()) {
CONDITIONALLY_UNAVAILABLE
} else if (!isThreadSupportedOnDevice) {
UNSUPPORTED_ON_DEVICE
} else if (airplaneModeOn) {
DISABLED_DEPENDENT_SETTING
} else {
AVAILABLE
}
}
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)
}
override fun isChecked(): Boolean {
// TODO (b/322742298):
// Check airplane mode here because it's planned to disable Thread state in airplane mode
// (code in the mainline module). But it's currently not implemented yet (b/322742298).
// By design, the toggle should be unchecked in airplane mode, so explicitly check the
// airplane mode here to acchieve the same UX.
return !airplaneModeOn && threadEnabled
}
override fun setChecked(isChecked: Boolean): Boolean {
if (threadController == null) {
return false
}
val action = if (isChecked) "enable" else "disable"
threadController.setEnabled(
isChecked,
executor,
object : OutcomeReceiver<Void?, ThreadNetworkException> {
override fun onError(e: ThreadNetworkException) {
// TODO(b/327549838): gracefully handle the failure by resetting the UI state
Log.e(TAG, "Failed to $action Thread", e)
}
override fun onResult(unused: Void?) {
Log.d(TAG, "Successfully $action Thread")
}
})
return true
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (threadController == null) {
return
}
when (event) {
Lifecycle.Event.ON_START -> {
threadController.registerStateCallback(executor, stateCallback)
airplaneModeOn = isAirplaneModeOn(mContext)
mContext.registerReceiver(
airplaneModeReceiver,
IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
)
preference?.let { preference -> updateState(preference) }
}
Lifecycle.Event.ON_STOP -> {
threadController.unregisterStateCallback(stateCallback)
mContext.unregisterReceiver(airplaneModeReceiver)
}
else -> {}
}
}
override fun updateState(preference: Preference) {
super.updateState(preference)
preference.isEnabled = !airplaneModeOn
refreshSummary(preference)
}
override fun getSummary(): CharSequence {
val resId: Int = if (airplaneModeOn) {
R.string.thread_network_settings_summary_airplane_mode
} else {
R.string.thread_network_settings_summary
}
return mContext.getResources().getString(resId)
}
override fun getSliceHighlightMenuRes(): Int {
return R.string.menu_key_connected_devices
}
companion object {
private const val TAG = "ThreadNetworkSettings"
private fun getThreadNetworkController(context: Context): BaseThreadNetworkController? {
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_THREAD_NETWORK)) {
return null
}
val manager = context.getSystemService(ThreadNetworkManager::class.java) ?: return null
val controller = manager.allThreadNetworkControllers[0]
return object : BaseThreadNetworkController {
override fun setEnabled(
enabled: Boolean,
executor: Executor,
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
) {
controller.setEnabled(enabled, executor, receiver)
}
override fun registerStateCallback(executor: Executor, callback: StateCallback) {
controller.registerStateCallback(executor, callback)
}
override fun unregisterStateCallback(callback: StateCallback) {
controller.unregisterStateCallback(callback)
}
}
}
private fun isAirplaneModeOn(context: Context): Boolean {
return Settings.Global.getInt(
context.contentResolver,
Settings.Global.AIRPLANE_MODE_ON,
0
) == 1
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.content.Context
import android.net.thread.ThreadNetworkController
import android.net.thread.ThreadNetworkController.StateCallback
import android.net.thread.ThreadNetworkException
import android.os.OutcomeReceiver
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.TogglePreferenceController
import com.android.settings.flags.Flags
import java.util.concurrent.Executor
/**
* Controller for the "Use Thread" toggle in "Connected devices > Connection preferences > Thread".
*/
class ThreadNetworkToggleController @VisibleForTesting constructor(
context: Context,
key: String,
private val executor: Executor,
private val threadController: BaseThreadNetworkController?
) : TogglePreferenceController(context, key), LifecycleEventObserver {
private val stateCallback: StateCallback
private var threadEnabled = false
private var preference: Preference? = null
constructor(context: Context, key: String) : this(
context,
key,
ContextCompat.getMainExecutor(context),
ThreadNetworkUtils.getThreadNetworkController(context)
)
init {
stateCallback = newStateCallback()
}
val isThreadSupportedOnDevice: Boolean
get() = threadController != null
private fun newStateCallback(): StateCallback {
return object : StateCallback {
override fun onThreadEnableStateChanged(enabledState: Int) {
threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
preference?.let { preference -> updateState(preference) }
}
override fun onDeviceRoleChanged(role: Int) {}
}
}
override fun getAvailabilityStatus(): Int {
return if (!Flags.threadSettingsEnabled()) {
CONDITIONALLY_UNAVAILABLE
} else if (!isThreadSupportedOnDevice) {
UNSUPPORTED_ON_DEVICE
} else {
AVAILABLE
}
}
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)
}
override fun isChecked(): Boolean {
return threadEnabled
}
override fun setChecked(isChecked: Boolean): Boolean {
if (threadController == null) {
return false
}
// Avoids dead loop of setChecked -> threadController.setEnabled() ->
// StateCallback.onThreadEnableStateChanged -> updateState -> setChecked
if (isChecked == isChecked()) {
return true
}
val action = if (isChecked) "enable" else "disable"
threadController.setEnabled(
isChecked,
executor,
object : OutcomeReceiver<Void?, ThreadNetworkException> {
override fun onError(e: ThreadNetworkException) {
// TODO(b/327549838): gracefully handle the failure by resetting the UI state
Log.e(TAG, "Failed to $action Thread", e)
}
override fun onResult(unused: Void?) {
Log.d(TAG, "Successfully $action Thread")
}
})
return true
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (threadController == null) {
return
}
when (event) {
Lifecycle.Event.ON_START -> {
threadController.registerStateCallback(executor, stateCallback)
}
Lifecycle.Event.ON_STOP -> {
threadController.unregisterStateCallback(stateCallback)
}
else -> {}
}
}
override fun getSliceHighlightMenuRes(): Int {
return R.string.menu_key_connected_devices
}
companion object {
private const val TAG = "ThreadNetworkSettings"
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.threadnetwork
import android.content.Context
import android.content.pm.PackageManager
import android.net.thread.ThreadNetworkController
import android.net.thread.ThreadNetworkController.StateCallback
import android.net.thread.ThreadNetworkException
import android.net.thread.ThreadNetworkManager
import android.os.OutcomeReceiver
import androidx.annotation.VisibleForTesting
import java.util.concurrent.Executor
/** Common utilities for Thread settings classes. */
object ThreadNetworkUtils {
/**
* Retrieves the [BaseThreadNetworkController] instance that is backed by the Android
* [ThreadNetworkController].
*/
fun getThreadNetworkController(context: Context): BaseThreadNetworkController? {
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_THREAD_NETWORK)) {
return null
}
val manager = context.getSystemService(ThreadNetworkManager::class.java) ?: return null
val controller = manager.allThreadNetworkControllers[0]
return object : BaseThreadNetworkController {
override fun setEnabled(
enabled: Boolean,
executor: Executor,
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
) {
controller.setEnabled(enabled, executor, receiver)
}
override fun registerStateCallback(executor: Executor, callback: StateCallback) {
controller.registerStateCallback(executor, callback)
}
override fun unregisterStateCallback(callback: StateCallback) {
controller.unregisterStateCallback(callback)
}
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.core;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.theme.R;
import java.util.ArrayList;
import java.util.List;
public class RoundCornerPreferenceAdapter extends PreferenceGroupAdapter {
private static final int ROUND_CORNER_CENTER = 1;
private static final int ROUND_CORNER_TOP = 1 << 1;
private static final int ROUND_CORNER_BOTTOM = 1 << 2;
private final PreferenceGroup mPreferenceGroup;
private List<Integer> mRoundCornerMappingList;
private final Handler mHandler;
private final Runnable mSyncRunnable = new Runnable() {
@Override
public void run() {
updatePreferences();
}
};
public RoundCornerPreferenceAdapter(@NonNull PreferenceGroup preferenceGroup) {
super(preferenceGroup);
mPreferenceGroup = preferenceGroup;
mHandler = new Handler(Looper.getMainLooper());
updatePreferences();
}
@Override
public void onPreferenceHierarchyChange(@NonNull Preference preference) {
super.onPreferenceHierarchyChange(preference);
mHandler.removeCallbacks(mSyncRunnable);
mHandler.post(mSyncRunnable);
}
@Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
updateBackground(holder, position);
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
private void updatePreferences() {
mRoundCornerMappingList = new ArrayList<>();
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup);
}
private void mappingPreferenceGroup(List<Integer> visibleList, PreferenceGroup group) {
int groupSize = group.getPreferenceCount();
int firstVisible = 0;
int lastVisible = 0;
for (int i = 0; i < groupSize; i++) {
Preference pref = group.getPreference(i);
if (!pref.isVisible()) {
continue;
}
//the first visible preference.
Preference firstVisiblePref = group.getPreference(firstVisible);
if (!firstVisiblePref.isVisible()) {
firstVisible = i;
}
int value = 0;
if (group instanceof PreferenceCategory) {
if (pref instanceof PreferenceCategory) {
visibleList.add(value);
mappingPreferenceGroup(visibleList, (PreferenceCategory) pref);
} else {
if (i == firstVisible) {
value |= ROUND_CORNER_TOP;
}
value |= ROUND_CORNER_BOTTOM;
if (i > lastVisible) {
// the last
int lastIndex = visibleList.size() - 1;
int newValue = visibleList.get(lastIndex) & ~ROUND_CORNER_BOTTOM;
visibleList.set(lastIndex, newValue);
lastVisible = i;
}
value |= ROUND_CORNER_CENTER;
visibleList.add(value);
}
} else {
visibleList.add(value);
if (pref instanceof PreferenceCategory) {
mappingPreferenceGroup(visibleList, (PreferenceCategory) pref);
}
}
}
}
/** handle roundCorner background */
private void updateBackground(PreferenceViewHolder holder, int position) {
int CornerType = mRoundCornerMappingList.get(position);
if ((CornerType & ROUND_CORNER_CENTER) == 0) {
return;
}
View v = holder.itemView;
if (((CornerType & ROUND_CORNER_TOP) != 0) && ((CornerType & ROUND_CORNER_BOTTOM) == 0)) {
// the first
v.setBackgroundResource(R.drawable.settingslib_round_background_top);
} else if (((CornerType & ROUND_CORNER_BOTTOM) != 0)
&& ((CornerType & ROUND_CORNER_TOP) == 0)) {
// the last
v.setBackgroundResource(R.drawable.settingslib_round_background_bottom);
} else if (((CornerType & ROUND_CORNER_TOP) != 0)
&& ((CornerType & ROUND_CORNER_BOTTOM) != 0)) {
// the only one preference
v.setBackgroundResource(R.drawable.settingslib_round_background);
} else {
// in the center
v.setBackgroundResource(R.drawable.settingslib_round_background_center);
}
}
}

View File

@@ -30,7 +30,6 @@ import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard; import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard;
import com.android.settings.accessibility.CaptioningPropertiesFragment; import com.android.settings.accessibility.CaptioningPropertiesFragment;
import com.android.settings.accessibility.ColorAndMotionFragment; import com.android.settings.accessibility.ColorAndMotionFragment;
import com.android.settings.accessibility.ColorContrastFragment;
import com.android.settings.accessibility.HearingDevicePairingFragment; import com.android.settings.accessibility.HearingDevicePairingFragment;
import com.android.settings.accessibility.TextReadingPreferenceFragment; import com.android.settings.accessibility.TextReadingPreferenceFragment;
import com.android.settings.accessibility.TextReadingPreferenceFragmentForSetupWizard; import com.android.settings.accessibility.TextReadingPreferenceFragmentForSetupWizard;
@@ -105,6 +104,7 @@ import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFragment;
import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings; import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard; import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
import com.android.settings.display.AutoBrightnessSettings; import com.android.settings.display.AutoBrightnessSettings;
import com.android.settings.display.ColorContrastFragment;
import com.android.settings.display.NightDisplaySettings; import com.android.settings.display.NightDisplaySettings;
import com.android.settings.display.ScreenTimeoutSettings; import com.android.settings.display.ScreenTimeoutSettings;
import com.android.settings.display.SmartAutoRotatePreferenceFragment; import com.android.settings.display.SmartAutoRotatePreferenceFragment;

View File

@@ -444,7 +444,9 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
} }
if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) { if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
iconDrawable.setTint(Utils.getHomepageIconColor(preference.getContext())); iconDrawable.setTint(Utils.getHomepageIconColor(preference.getContext()));
} else if (forceRoundedIcon && !TextUtils.equals(mContext.getPackageName(), iconPackage)) { }
if (forceRoundedIcon && !TextUtils.equals(mContext.getPackageName(), iconPackage)) {
iconDrawable = new AdaptiveIcon(mContext, iconDrawable, iconDrawable = new AdaptiveIcon(mContext, iconDrawable,
R.dimen.dashboard_tile_foreground_image_inset); R.dimen.dashboard_tile_foreground_image_inset);
((AdaptiveIcon) iconDrawable).setBackgroundColor(mContext, tile); ((AdaptiveIcon) iconDrawable).setBackgroundColor(mContext, tile);

View File

@@ -26,11 +26,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R import com.android.settings.R
import com.android.settings.core.SubSettingLauncher import com.android.settings.core.SubSettingLauncher
import com.android.settings.datausage.lib.BillingCycleRepository import com.android.settings.datausage.lib.BillingCycleRepository
import com.android.settings.network.mobileDataEnabledFlow
import com.android.settings.spa.preference.ComposePreference import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.PreferenceModel
import kotlinx.coroutines.flow.map
/** /**
* Preference which displays billing cycle of subscription * Preference which displays billing cycle of subscription
@@ -46,8 +44,8 @@ class BillingCyclePreference @JvmOverloads constructor(
override fun setTemplate(template: NetworkTemplate, subId: Int) { override fun setTemplate(template: NetworkTemplate, subId: Int) {
setContent { setContent {
val isModifiable by remember { val isModifiable by remember(subId) {
context.mobileDataEnabledFlow(subId).map { repository.isModifiable(subId) } repository.isModifiableFlow(subId)
}.collectAsStateWithLifecycle(initialValue = false) }.collectAsStateWithLifecycle(initialValue = false)
Preference(object : PreferenceModel { Preference(object : PreferenceModel {

View File

@@ -35,7 +35,7 @@ import com.android.settings.datausage.lib.BillingCycleRepository
import com.android.settings.datausage.lib.NetworkUsageData import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settings.network.MobileNetworkRepository import com.android.settings.network.MobileNetworkRepository
import com.android.settings.network.SubscriptionUtil import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.mobileDataEnabledFlow import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.framework.common.userManager
@@ -113,8 +113,8 @@ open class DataUsageList : DashboardFragment() {
override fun onViewCreated(v: View, savedInstanceState: Bundle?) { override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
super.onViewCreated(v, savedInstanceState) super.onViewCreated(v, savedInstanceState)
requireContext().mobileDataEnabledFlow(subId) billingCycleRepository.isModifiableFlow(subId)
.collectLatestWithLifecycle(viewLifecycleOwner) { updatePolicy() } .collectLatestWithLifecycle(viewLifecycleOwner, action = ::updatePolicy)
val template = template ?: return val template = template ?: return
viewModel.templateFlow.value = template viewModel.templateFlow.value = template
@@ -163,16 +163,14 @@ open class DataUsageList : DashboardFragment() {
} }
/** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */ /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
private fun updatePolicy() { private fun updatePolicy(isModifiable: Boolean) {
val isBillingCycleModifiable = isBillingCycleModifiable() val isBillingCycleModifiable = isModifiable && isActiveSubscription()
dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable) dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable)
chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable) chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable)
} }
private fun isBillingCycleModifiable(): Boolean = private fun isActiveSubscription(): Boolean =
billingCycleRepository.isModifiable(subId) && requireContext().requireSubscriptionManager().getActiveSubscriptionInfo(subId) != null
requireContext().getSystemService(SubscriptionManager::class.java)!!
.getActiveSubscriptionInfo(subId) != null
/** /**
* Updates the chart and detail data when initial loaded or selected cycle changed. * Updates the chart and detail data when initial loaded or selected cycle changed.

View File

@@ -19,10 +19,15 @@ package com.android.settings.datausage.lib
import android.content.Context import android.content.Context
import android.os.INetworkManagementService import android.os.INetworkManagementService
import android.os.ServiceManager import android.os.ServiceManager
import android.telephony.TelephonyManager
import android.util.Log import android.util.Log
import androidx.annotation.OpenForTesting import androidx.annotation.OpenForTesting
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@OpenForTesting @OpenForTesting
open class BillingCycleRepository @JvmOverloads constructor( open class BillingCycleRepository @JvmOverloads constructor(
@@ -31,12 +36,14 @@ open class BillingCycleRepository @JvmOverloads constructor(
INetworkManagementService.Stub.asInterface( INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
), ),
private val telephonyRepository: TelephonyRepository = TelephonyRepository(context),
) { ) {
private val userManager = context.userManager private val userManager = context.userManager
private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
fun isModifiable(subId: Int): Boolean = fun isModifiableFlow(subId: Int): Flow<Boolean> =
isBandwidthControlEnabled() && userManager.isAdminUser && isDataEnabled(subId) telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled ->
isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
}.conflate().flowOn(Dispatchers.Default)
open fun isBandwidthControlEnabled(): Boolean = try { open fun isBandwidthControlEnabled(): Boolean = try {
networkService.isBandwidthControlEnabled networkService.isBandwidthControlEnabled
@@ -45,10 +52,6 @@ open class BillingCycleRepository @JvmOverloads constructor(
false false
} }
private fun isDataEnabled(subId: Int): Boolean =
telephonyManager.createForSubscriptionId(subId)
.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
companion object { companion object {
private const val TAG = "BillingCycleRepository" private const val TAG = "BillingCycleRepository"
} }

View File

@@ -69,12 +69,19 @@ public class GrammaticalGenderPreferenceController extends DeveloperOptionsPrefe
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final var oldValue = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED);
SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, newValue.toString()); SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, newValue.toString());
updateState(mPreference); updateState(mPreference);
try { try {
Configuration config = mActivityManager.getConfiguration(); Configuration config = mActivityManager.getConfiguration();
config.setGrammaticalGender(Integer.parseInt(newValue.toString())); // Only apply the developer settings value if it is the one currently used,
mActivityManager.updatePersistentConfiguration(config); // otherwise it means there's some kind of override that we don't want to
// touch here.
if (config.getGrammaticalGender() == oldValue) {
config.setGrammaticalGender(Integer.parseInt(newValue.toString()));
mActivityManager.updatePersistentConfiguration(config);
}
} catch (RemoteException ex) { } catch (RemoteException ex) {
// intentional no-op // intentional no-op
} }

View File

@@ -14,13 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityFooterPreferenceController;
/** Preference controller for footer in color contrast page. */ /** Preference controller for footer in color contrast page. */
public class ColorContrastFooterPreferenceController extends public class ColorContrastFooterPreferenceController extends

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
@@ -29,7 +29,6 @@ public class ColorContrastFragment extends DashboardFragment {
private static final String TAG = "ColorContrastFragment"; private static final String TAG = "ColorContrastFragment";
@Override @Override
protected int getPreferenceScreenResId() { protected int getPreferenceScreenResId() {
return R.xml.accessibility_color_contrast; return R.xml.accessibility_color_contrast;

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD;
import static android.app.UiModeManager.ContrastUtils.toContrastLevel;
import android.app.UiModeManager;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settings.accessibility.Flags;
import com.android.settings.core.BasePreferenceController;
import java.util.Map;
/**
* Controller for {@link ColorContrastFragment}.
*/
public class ContrastPreferenceController extends BasePreferenceController {
public ContrastPreferenceController(@NonNull Context context, @NonNull String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return Flags.enableColorContrastControl() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public CharSequence getSummary() {
Map<Integer, Integer> mContrastLevelToResId = Map.ofEntries(
Map.entry(CONTRAST_LEVEL_STANDARD, R.string.contrast_default),
Map.entry(CONTRAST_LEVEL_MEDIUM, R.string.contrast_medium),
Map.entry(CONTRAST_LEVEL_HIGH, R.string.contrast_high)
);
float contrastLevel = mContext.getSystemService(UiModeManager.class).getContrast();
return mContext.getString(mContrastLevelToResId.get(toContrastLevel(contrastLevel)));
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH; import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM; import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM;

View File

@@ -649,9 +649,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private final class HourlyChartLabelTextGenerator extends BaseLabelTextGenerator private final class HourlyChartLabelTextGenerator extends BaseLabelTextGenerator
implements BatteryChartViewModel.LabelTextGenerator { implements BatteryChartViewModel.LabelTextGenerator {
private static final int FULL_CHARGE_BATTERY_LEVEL = 100; private boolean mIsStartTimestamp;
private boolean mIsFromFullCharge;
private long mFistTimestamp; private long mFistTimestamp;
private long mLatestTimestamp; private long mLatestTimestamp;
@@ -664,7 +662,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
long timestamp = timestamps.get(index); long timestamp = timestamps.get(index);
boolean showMinute = false; boolean showMinute = false;
if (Objects.equal(timestamp, mFistTimestamp)) { if (Objects.equal(timestamp, mFistTimestamp)) {
if (mIsFromFullCharge) { if (mIsStartTimestamp) {
showMinute = true; showMinute = true;
} else { } else {
// starts from 7 days ago // starts from 7 days ago
@@ -699,8 +697,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
@NonNull final BatteryLevelData batteryLevelData) { @NonNull final BatteryLevelData batteryLevelData) {
BatteryLevelData.PeriodBatteryLevelData firstDayLevelData = BatteryLevelData.PeriodBatteryLevelData firstDayLevelData =
batteryLevelData.getHourlyBatteryLevelsPerDay().get(0); batteryLevelData.getHourlyBatteryLevelsPerDay().get(0);
this.mIsFromFullCharge = this.mIsStartTimestamp = firstDayLevelData.isStartTimestamp();
firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL;
this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0); this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0);
this.mLatestTimestamp = this.mLatestTimestamp =
getLast( getLast(

View File

@@ -28,6 +28,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.util.Preconditions; import androidx.core.util.Preconditions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -39,17 +40,24 @@ public final class BatteryLevelData {
private static final long MIN_SIZE = 2; private static final long MIN_SIZE = 2;
private static final long TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2; private static final long TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
// For testing only.
@VisibleForTesting @Nullable static Calendar sTestCalendar;
/** A container for the battery timestamp and level data. */ /** A container for the battery timestamp and level data. */
public static final class PeriodBatteryLevelData { public static final class PeriodBatteryLevelData {
// The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when // The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when
// there is no level data for the corresponding timestamp. // there is no level data for the corresponding timestamp.
private final List<Long> mTimestamps; private final List<Long> mTimestamps;
private final List<Integer> mLevels; private final List<Integer> mLevels;
private final boolean mIsStartTimestamp;
public PeriodBatteryLevelData( public PeriodBatteryLevelData(
@NonNull Map<Long, Integer> batteryLevelMap, @NonNull List<Long> timestamps) { @NonNull Map<Long, Integer> batteryLevelMap,
@NonNull List<Long> timestamps,
boolean isStartTimestamp) {
mTimestamps = timestamps; mTimestamps = timestamps;
mLevels = new ArrayList<>(timestamps.size()); mLevels = new ArrayList<>(timestamps.size());
mIsStartTimestamp = isStartTimestamp;
for (Long timestamp : timestamps) { for (Long timestamp : timestamps) {
mLevels.add( mLevels.add(
batteryLevelMap.containsKey(timestamp) batteryLevelMap.containsKey(timestamp)
@@ -66,6 +74,10 @@ public final class BatteryLevelData {
return mLevels; return mLevels;
} }
public boolean isStartTimestamp() {
return mIsStartTimestamp;
}
@Override @Override
public String toString() { public String toString() {
return String.format( return String.format(
@@ -105,14 +117,21 @@ public final class BatteryLevelData {
final List<Long> timestampList = new ArrayList<>(batteryLevelMap.keySet()); final List<Long> timestampList = new ArrayList<>(batteryLevelMap.keySet());
Collections.sort(timestampList); Collections.sort(timestampList);
final long minTimestamp = timestampList.get(0);
final long sixDaysAgoTimestamp =
DatabaseUtils.getTimestampSixDaysAgo(sTestCalendar != null ? sTestCalendar : null);
final boolean isStartTimestamp = minTimestamp > sixDaysAgoTimestamp;
final List<Long> dailyTimestamps = getDailyTimestamps(timestampList); final List<Long> dailyTimestamps = getDailyTimestamps(timestampList);
final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps); final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps);
mDailyBatteryLevels = new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps); mDailyBatteryLevels =
new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps, isStartTimestamp);
mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size()); mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size());
for (List<Long> hourlyTimestampsPerDay : hourlyTimestamps) { for (int i = 0; i < hourlyTimestamps.size(); i++) {
final List<Long> hourlyTimestampsPerDay = hourlyTimestamps.get(i);
mHourlyBatteryLevelsPerDay.add( mHourlyBatteryLevelsPerDay.add(
new PeriodBatteryLevelData(batteryLevelMap, hourlyTimestampsPerDay)); new PeriodBatteryLevelData(
batteryLevelMap, hourlyTimestampsPerDay, isStartTimestamp && i == 0));
} }
} }

View File

@@ -67,9 +67,13 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
refreshJobs(context); refreshJobs(context);
break; break;
case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_TIME_CHANGED:
Log.d(TAG, "refresh job and clear all data from action=" + action); Log.d(TAG, "refresh job and clear data from action=" + action);
DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context, intent); DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context, intent);
break; break;
case Intent.ACTION_TIMEZONE_CHANGED:
Log.d(TAG, "refresh job and clear all data from action=" + action);
DatabaseUtils.clearDataAfterTimeZoneChangedIfNeeded(context);
break;
default: default:
Log.w(TAG, "receive unsupported action=" + action); Log.w(TAG, "receive unsupported action=" + action);
} }

View File

@@ -16,8 +16,6 @@
package com.android.settings.fuelgauge.batteryusage; package com.android.settings.fuelgauge.batteryusage;
import static android.content.Intent.FLAG_RECEIVER_REPLACE_PENDING;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging; import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
import android.app.usage.IUsageStatsManager; import android.app.usage.IUsageStatsManager;
@@ -59,6 +57,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -436,6 +435,23 @@ public final class DatabaseUtils {
}); });
} }
/** Clears data after a specific startTimestamp in the battery usage database. */
public static void clearAllAfter(Context context, long startTimestamp) {
AsyncTask.execute(
() -> {
try {
final BatteryStateDatabase database =
BatteryStateDatabase.getInstance(context.getApplicationContext());
database.appUsageEventDao().clearAllAfter(startTimestamp);
database.batteryEventDao().clearAllAfter(startTimestamp);
database.batteryStateDao().clearAllAfter(startTimestamp);
database.batteryUsageSlotDao().clearAllAfter(startTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "clearAllAfter() failed", e);
}
});
}
/** Clears all out-of-date data in the battery usage database. */ /** Clears all out-of-date data in the battery usage database. */
public static void clearExpiredDataIfNeeded(Context context) { public static void clearExpiredDataIfNeeded(Context context) {
AsyncTask.execute( AsyncTask.execute(
@@ -456,14 +472,14 @@ public final class DatabaseUtils {
}); });
} }
/** Clears all data and jobs if current timestamp is out of the range of last recorded job. */ /** Clears data after new updated time and refresh periodic job. */
public static void clearDataAfterTimeChangedIfNeeded(Context context, Intent intent) { public static void clearDataAfterTimeChangedIfNeeded(Context context, Intent intent) {
if ((intent.getFlags() & FLAG_RECEIVER_REPLACE_PENDING) != 0) { if ((intent.hasExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT))) {
BatteryUsageLogUtils.writeLog( BatteryUsageLogUtils.writeLog(
context, context,
Action.TIME_UPDATED, Action.TIME_UPDATED,
"Database is not cleared because the time change intent is only" "Database is not cleared because the time change intent is"
+ " for the existing pending receiver."); + " for time format change");
return; return;
} }
AsyncTask.execute( AsyncTask.execute(
@@ -480,6 +496,22 @@ public final class DatabaseUtils {
}); });
} }
/** Clears all data and reset jobs if timezone changed. */
public static void clearDataAfterTimeZoneChangedIfNeeded(Context context) {
AsyncTask.execute(
() -> {
try {
clearDataAfterTimeZoneChangedIfNeededInternal(context);
} catch (RuntimeException e) {
Log.e(TAG, "clearDataAfterTimeZoneChangedIfNeeded() failed", e);
BatteryUsageLogUtils.writeLog(
context,
Action.TIMEZONE_UPDATED,
"clearDataAfterTimeZoneChangedIfNeeded() failed" + e);
}
});
}
/** Returns the timestamp for 00:00 6 days before the calendar date. */ /** Returns the timestamp for 00:00 6 days before the calendar date. */
public static long getTimestampSixDaysAgo(Calendar calendar) { public static long getTimestampSixDaysAgo(Calendar calendar) {
Calendar startCalendar = Calendar startCalendar =
@@ -861,37 +893,38 @@ public final class DatabaseUtils {
} }
private static void clearDataAfterTimeChangedIfNeededInternal(Context context) { private static void clearDataAfterTimeChangedIfNeededInternal(Context context) {
final long currentTime = System.currentTimeMillis();
final String logInfo =
String.format(Locale.ENGLISH, "clear data after current time = %d", currentTime);
Log.d(TAG, logInfo);
BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
DatabaseUtils.clearAllAfter(context, currentTime);
PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
final List<BatteryEvent> batteryLevelRecordEvents = final List<BatteryEvent> batteryLevelRecordEvents =
DatabaseUtils.getBatteryEvents( DatabaseUtils.getBatteryEvents(
context, context,
Calendar.getInstance(), Calendar.getInstance(),
getLastFullChargeTime(context), getLastFullChargeTime(context),
BATTERY_LEVEL_RECORD_EVENTS); BATTERY_LEVEL_RECORD_EVENTS);
final long lastRecordTimestamp = if (batteryLevelRecordEvents.isEmpty()) {
batteryLevelRecordEvents.isEmpty() // Take a snapshot of battery usage data immediately if there's no battery events.
? INVALID_TIMESTAMP BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
: batteryLevelRecordEvents.get(0).getTimestamp(); }
final long nextRecordTimestamp = }
TimestampUtils.getNextEvenHourTimestamp(lastRecordTimestamp);
final long currentTime = System.currentTimeMillis(); private static void clearDataAfterTimeZoneChangedIfNeededInternal(Context context) {
final boolean isOutOfTimeRange =
lastRecordTimestamp == INVALID_TIMESTAMP
|| currentTime < lastRecordTimestamp
|| currentTime > nextRecordTimestamp;
final String logInfo = final String logInfo =
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"clear database = %b, current time = %d, last record time = %d", "clear database for new time zone = %s",
isOutOfTimeRange, TimeZone.getDefault().toString());
currentTime, BatteryUsageLogUtils.writeLog(context, Action.TIMEZONE_UPDATED, logInfo);
lastRecordTimestamp);
Log.d(TAG, logInfo); Log.d(TAG, logInfo);
BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo); DatabaseUtils.clearAll(context);
if (isOutOfTimeRange) { PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
DatabaseUtils.clearAll(context); // Take a snapshot of battery usage data immediately
PeriodicJobManager.getInstance(context) BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
.refreshJob(/* fromBoot= */ false);
}
} }
private static long loadLongFromContentProvider( private static long loadLongFromContentProvider(

View File

@@ -55,6 +55,10 @@ public interface AppUsageEventDao {
@Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp") @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp); void clearAllBefore(long timestamp);
/** Deletes all recorded data after a specific timestamp. */
@Query("DELETE FROM AppUsageEventEntity WHERE timestamp >= :timestamp")
void clearAllAfter(long timestamp);
/** Clears all recorded data in the database. */ /** Clears all recorded data in the database. */
@Query("DELETE FROM AppUsageEventEntity") @Query("DELETE FROM AppUsageEventEntity")
void clearAll(); void clearAll();

View File

@@ -65,6 +65,10 @@ public interface BatteryEventDao {
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp") @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp); void clearAllBefore(long timestamp);
/** Deletes all recorded data after a specific timestamp. */
@Query("DELETE FROM BatteryEventEntity WHERE timestamp >= :timestamp")
void clearAllAfter(long timestamp);
/** Clears all recorded data in the database. */ /** Clears all recorded data in the database. */
@Query("DELETE FROM BatteryEventEntity") @Query("DELETE FROM BatteryEventEntity")
void clearAll(); void clearAll();

View File

@@ -61,6 +61,10 @@ public interface BatteryStateDao {
@Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp") @Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp); void clearAllBefore(long timestamp);
/** Deletes all recorded data after a specific timestamp. */
@Query("DELETE FROM BatteryState WHERE timestamp >= :timestamp")
void clearAllAfter(long timestamp);
/** Clears all recorded data in the database. */ /** Clears all recorded data in the database. */
@Query("DELETE FROM BatteryState") @Query("DELETE FROM BatteryState")
void clearAll(); void clearAll();

View File

@@ -52,6 +52,10 @@ public interface BatteryUsageSlotDao {
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp") @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp); void clearAllBefore(long timestamp);
/** Deletes all recorded data after a specific timestamp. */
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp")
void clearAllAfter(long timestamp);
/** Clears all recorded data in the database. */ /** Clears all recorded data in the database. */
@Query("DELETE FROM BatteryUsageSlotEntity") @Query("DELETE FROM BatteryUsageSlotEntity")
void clearAll(); void clearAll();

View File

@@ -73,6 +73,7 @@ import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils; import com.android.settings.activityembedding.EmbeddedDeepLinkUtils;
import com.android.settings.core.CategoryMixin; import com.android.settings.core.CategoryMixin;
import com.android.settings.core.FeatureFlags; import com.android.settings.core.FeatureFlags;
import com.android.settings.flags.Flags;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
@@ -160,8 +161,12 @@ public class SettingsHomepageActivity extends FragmentActivity implements
if (mAllowUpdateSuggestion) { if (mAllowUpdateSuggestion) {
Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion); Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion);
mAllowUpdateSuggestion = false; mAllowUpdateSuggestion = false;
mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); if (Flags.homepageRevamp()) {
mTwoPaneSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
} else {
mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
mTwoPaneSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
}
} }
if (mHomepageView == null) { if (mHomepageView == null) {
@@ -245,7 +250,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements
} }
setupEdgeToEdge(); setupEdgeToEdge();
setContentView(R.layout.settings_homepage_container); setContentView(
Flags.homepageRevamp()
? R.layout.settings_homepage_container_v2
: R.layout.settings_homepage_container);
mIsTwoPane = ActivityEmbeddingUtils.isAlreadyEmbedded(this); mIsTwoPane = ActivityEmbeddingUtils.isAlreadyEmbedded(this);
@@ -397,19 +405,31 @@ public class SettingsHomepageActivity extends FragmentActivity implements
} }
private void initSearchBarView() { private void initSearchBarView() {
final Toolbar toolbar = findViewById(R.id.search_action_bar); if (Flags.homepageRevamp()) {
FeatureFactory.getFeatureFactory().getSearchFeatureProvider() Toolbar toolbar = findViewById(R.id.search_action_bar_unified);
.initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
if (mIsEmbeddingActivityEnabled) {
final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane);
FeatureFactory.getFeatureFactory().getSearchFeatureProvider() FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
.initSearchToolbar(this /* activity */, toolbarTwoPaneVersion, .initSearchToolbar(this /* activity */, toolbar,
SettingsEnums.SETTINGS_HOMEPAGE); SettingsEnums.SETTINGS_HOMEPAGE);
} else {
final Toolbar toolbar = findViewById(R.id.search_action_bar);
FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
.initSearchToolbar(this /* activity */, toolbar,
SettingsEnums.SETTINGS_HOMEPAGE);
if (mIsEmbeddingActivityEnabled) {
final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane);
FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
.initSearchToolbar(this /* activity */, toolbarTwoPaneVersion,
SettingsEnums.SETTINGS_HOMEPAGE);
}
} }
} }
private void initAvatarView() { private void initAvatarView() {
if (Flags.homepageRevamp()) {
return;
}
final ImageView avatarView = findViewById(R.id.account_avatar); final ImageView avatarView = findViewById(R.id.account_avatar);
final ImageView avatarTwoPaneView = findViewById(R.id.account_avatar_two_pane_version); final ImageView avatarTwoPaneView = findViewById(R.id.account_avatar_two_pane_version);
if (AvatarViewMixin.isAvatarSupported(this)) { if (AvatarViewMixin.isAvatarSupported(this)) {
@@ -458,8 +478,12 @@ public class SettingsHomepageActivity extends FragmentActivity implements
return; return;
} }
mSuggestionView = findViewById(R.id.suggestion_content); if (Flags.homepageRevamp()) {
mTwoPaneSuggestionView = findViewById(R.id.two_pane_suggestion_content); mSuggestionView = findViewById(R.id.unified_suggestion_content);
} else {
mSuggestionView = findViewById(R.id.suggestion_content);
mTwoPaneSuggestionView = findViewById(R.id.two_pane_suggestion_content);
}
mHomepageView = findViewById(R.id.settings_homepage_container); mHomepageView = findViewById(R.id.settings_homepage_container);
// Hide the homepage for preparing the suggestion. If scrolling is needed, the list views // Hide the homepage for preparing the suggestion. If scrolling is needed, the list views
// should be initialized in the invisible homepage view to prevent a scroll flicker. // should be initialized in the invisible homepage view to prevent a scroll flicker.
@@ -467,11 +491,16 @@ public class SettingsHomepageActivity extends FragmentActivity implements
// Schedule a timer to show the homepage and hide the suggestion on timeout. // Schedule a timer to show the homepage and hide the suggestion on timeout.
mHomepageView.postDelayed(() -> showHomepageWithSuggestion(false), mHomepageView.postDelayed(() -> showHomepageWithSuggestion(false),
HOMEPAGE_LOADING_TIMEOUT_MS); HOMEPAGE_LOADING_TIMEOUT_MS);
showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ false), if (Flags.homepageRevamp()) {
R.id.suggestion_content); showFragment(new SuggestionFragCreator(fragmentClass, true),
if (mIsEmbeddingActivityEnabled) { R.id.unified_suggestion_content);
showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ true), } else {
R.id.two_pane_suggestion_content); showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ false),
R.id.suggestion_content);
if (mIsEmbeddingActivityEnabled) {
showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ true),
R.id.two_pane_suggestion_content);
}
} }
} }
@@ -736,7 +765,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
} }
private void updateHomepageAppBar() { private void updateHomepageAppBar() {
if (!mIsEmbeddingActivityEnabled) { if (Flags.homepageRevamp() || !mIsEmbeddingActivityEnabled) {
return; return;
} }
updateAppBarMinHeight(); updateAppBarMinHeight();
@@ -752,7 +781,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
} }
private void updateHomepagePaddings() { private void updateHomepagePaddings() {
if (!mIsEmbeddingActivityEnabled) { if (Flags.homepageRevamp() || !mIsEmbeddingActivityEnabled) {
return; return;
} }
if (mIsTwoPane) { if (mIsTwoPane) {
@@ -766,6 +795,9 @@ public class SettingsHomepageActivity extends FragmentActivity implements
} }
private void updateAppBarMinHeight() { private void updateAppBarMinHeight() {
if (Flags.homepageRevamp()) {
return;
}
final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height); final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height);
final int margin = getResources().getDimensionPixelSize( final int margin = getResources().getDimensionPixelSize(
mIsEmbeddingActivityEnabled && mIsTwoPane mIsEmbeddingActivityEnabled && mIsTwoPane

View File

@@ -42,8 +42,10 @@ import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.core.RoundCornerPreferenceAdapter;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.support.SupportPreferenceController; import com.android.settings.support.SupportPreferenceController;
@@ -84,7 +86,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
@Override @Override
protected int getPreferenceScreenResId() { protected int getPreferenceScreenResId() {
return R.xml.top_level_settings; return Flags.homepageRevamp() ? R.xml.top_level_settings_v2 : R.xml.top_level_settings;
} }
@Override @Override
@@ -331,10 +333,14 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
@Override @Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) { if (mIsEmbeddingActivityEnabled && (getActivity() instanceof SettingsHomepageActivity)) {
return super.onCreateAdapter(preferenceScreen); return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
} }
return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
if (Flags.homepageRevamp()) {
return new RoundCornerPreferenceAdapter(preferenceScreen);
}
return super.onCreateAdapter(preferenceScreen);
} }
@Override @Override
@@ -376,7 +382,10 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
} }
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.top_level_settings) { new BaseSearchIndexProvider(
Flags.homepageRevamp()
? R.xml.top_level_settings_v2
: R.xml.top_level_settings) {
@Override @Override
protected boolean isPageSearchEnabled(Context context) { protected boolean isPageSearchEnabled(Context context) {

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony;
import android.content.Context;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import com.android.settings.network.SubscriptionUtil;
/**
* Shows information about disable a physical SIM.
*/
public class DisableSimFooterPreferenceController extends TelephonyBasePreferenceController {
/**
* Constructor
*/
public DisableSimFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
/**
* re-init for SIM based on given subscription ID.
* @param subId is the given subscription ID
*/
public void init(int subId) {
mSubId = subId;
}
@Override
public int getAvailabilityStatus(int subId) {
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return CONDITIONALLY_UNAVAILABLE;
}
SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
if (info.getSubscriptionId() == subId) {
if (info.isEmbedded() || SubscriptionUtil.showToggleForPhysicalSim(subManager)) {
return CONDITIONALLY_UNAVAILABLE;
}
break;
}
}
return AVAILABLE;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionManager
/**
* Shows information about disable a physical SIM.
*/
class DisableSimFooterPreferenceController @JvmOverloads constructor(
context: Context,
preferenceKey: String,
private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context),
) : TelephonyBasePreferenceController(context, preferenceKey) {
/**
* Re-init for SIM based on given subscription ID.
*
* @param subId is the given subscription ID
*/
fun init(subId: Int) {
mSubId = subId
}
override fun getAvailabilityStatus(subId: Int): Int {
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID ||
subscriptionRepository.canDisablePhysicalSubscription()
) {
return CONDITIONALLY_UNAVAILABLE
}
val isAvailable =
subscriptionRepository.getSelectableSubscriptionInfoList().any { subInfo ->
subInfo.subscriptionId == subId && !subInfo.isEmbedded
}
return if (isAvailable) AVAILABLE else CONDITIONALLY_UNAVAILABLE
}
}

View File

@@ -48,13 +48,12 @@ import com.android.internal.telephony.flags.Flags;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.telephony.scan.NetworkScanRepository; import com.android.settings.network.telephony.scan.NetworkScanRepository;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete;
import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import kotlin.Unit; import kotlin.Unit;
import java.util.ArrayList; import java.util.ArrayList;
@@ -83,7 +82,8 @@ public class NetworkSelectSettings extends DashboardFragment {
private View mProgressHeader; private View mProgressHeader;
private Preference mStatusMessagePreference; private Preference mStatusMessagePreference;
@VisibleForTesting @VisibleForTesting
List<CellInfo> mCellInfoList; @NonNull
List<CellInfo> mCellInfoList = ImmutableList.of();
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private TelephonyManager mTelephonyManager; private TelephonyManager mTelephonyManager;
private SatelliteManager mSatelliteManager; private SatelliteManager mSatelliteManager;
@@ -96,7 +96,6 @@ public class NetworkSelectSettings extends DashboardFragment {
private AtomicBoolean mShouldFilterOutSatellitePlmn = new AtomicBoolean(); private AtomicBoolean mShouldFilterOutSatellitePlmn = new AtomicBoolean();
private NetworkScanRepository mNetworkScanRepository; private NetworkScanRepository mNetworkScanRepository;
private boolean mUpdateScanResult = false;
private NetworkSelectRepository mNetworkSelectRepository; private NetworkSelectRepository mNetworkSelectRepository;
@@ -213,38 +212,16 @@ public class NetworkSelectSettings extends DashboardFragment {
} }
private void launchNetworkScan() { private void launchNetworkScan() {
setProgressBarVisible(true);
mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> { mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> {
if (!mUpdateScanResult) { if (isPreferenceScreenEnabled()) {
// Not update UI if not in scan mode. scanResultHandler(networkScanResult);
return Unit.INSTANCE;
}
if (networkScanResult instanceof NetworkScanCellInfos networkScanCellInfos) {
scanResultHandler(networkScanCellInfos.getCellInfos());
return Unit.INSTANCE;
}
if (!isPreferenceScreenEnabled()) {
clearPreferenceSummary();
enablePreferenceScreen(true);
} else if (networkScanResult instanceof NetworkScanComplete
&& mCellInfoList == null) {
// In case the scan timeout before getting any results
addMessagePreference(R.string.empty_networks_list);
} else if (networkScanResult instanceof NetworkScanError) {
addMessagePreference(R.string.network_query_error);
} }
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
} }
@Override
public void onStart() {
super.onStart();
setProgressBarVisible(true);
mUpdateScanResult = true;
}
/** /**
* Update forbidden PLMNs from the USIM App * Update forbidden PLMNs from the USIM App
*/ */
@@ -268,8 +245,6 @@ public class NetworkSelectSettings extends DashboardFragment {
return false; return false;
} }
mUpdateScanResult = false;
// Refresh the last selected item in case users reselect network. // Refresh the last selected item in case users reselect network.
clearPreferenceSummary(); clearPreferenceSummary();
if (mSelectedPreference != null) { if (mSelectedPreference != null) {
@@ -380,27 +355,19 @@ public class NetworkSelectSettings extends DashboardFragment {
} }
} }
@Keep
@VisibleForTesting @VisibleForTesting
protected void scanResultHandler(List<CellInfo> results) { protected void scanResultHandler(NetworkScanRepository.NetworkScanResult results) {
mCellInfoList = filterOutSatellitePlmn(results); mCellInfoList = filterOutSatellitePlmn(results.getCellInfos());
Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList));
if (mCellInfoList != null && mCellInfoList.size() != 0) { updateAllPreferenceCategory();
final NetworkOperatorPreference connectedPref = updateAllPreferenceCategory(); NetworkScanRepository.NetworkScanState state = results.getState();
if (connectedPref != null) { if (state == NetworkScanRepository.NetworkScanState.ERROR) {
// update selected preference instance into connected preference addMessagePreference(R.string.network_query_error);
if (mSelectedPreference != null) { } else if (mCellInfoList.isEmpty()) {
mSelectedPreference = connectedPref;
}
} else if (!isPreferenceScreenEnabled()) {
mSelectedPreference.setSummary(R.string.network_connecting);
}
enablePreferenceScreen(true);
} else if (isPreferenceScreenEnabled()) {
addMessagePreference(R.string.empty_networks_list); addMessagePreference(R.string.empty_networks_list);
// keep showing progress bar, it will be stopped when error or completed
setProgressBarVisible(true);
} }
// keep showing progress bar, it will be stopped when error or completed
setProgressBarVisible(state == NetworkScanRepository.NetworkScanState.ACTIVE);
} }
@Keep @Keep
@@ -417,11 +384,8 @@ public class NetworkSelectSettings extends DashboardFragment {
/** /**
* Update the content of network operators list. * Update the content of network operators list.
*
* @return preference which shows connected
*/ */
@Nullable private void updateAllPreferenceCategory() {
private NetworkOperatorPreference updateAllPreferenceCategory() {
int numberOfPreferences = mPreferenceCategory.getPreferenceCount(); int numberOfPreferences = mPreferenceCategory.getPreferenceCount();
// remove unused preferences // remove unused preferences
@@ -432,7 +396,6 @@ public class NetworkSelectSettings extends DashboardFragment {
} }
// update the content of preference // update the content of preference
NetworkOperatorPreference connectedPref = null;
for (int index = 0; index < mCellInfoList.size(); index++) { for (int index = 0; index < mCellInfoList.size(); index++) {
final CellInfo cellInfo = mCellInfoList.get(index); final CellInfo cellInfo = mCellInfoList.get(index);
@@ -457,23 +420,10 @@ public class NetworkSelectSettings extends DashboardFragment {
if (mCellInfoList.get(index).isRegistered()) { if (mCellInfoList.get(index).isRegistered()) {
pref.setSummary(R.string.network_connected); pref.setSummary(R.string.network_connected);
connectedPref = pref;
} else { } else {
pref.setSummary(null); pref.setSummary(null);
} }
} }
// update selected preference instance by index
for (int index = 0; index < mCellInfoList.size(); index++) {
final CellInfo cellInfo = mCellInfoList.get(index);
if ((mSelectedPreference != null) && mSelectedPreference.isSameCell(cellInfo)) {
mSelectedPreference = (NetworkOperatorPreference)
(mPreferenceCategory.getPreference(index));
}
}
return connectedPref;
} }
/** /**
@@ -524,13 +474,6 @@ public class NetworkSelectSettings extends DashboardFragment {
} }
} }
private boolean isProgressBarVisible() {
if (mProgressHeader == null) {
return false;
}
return (mProgressHeader.getVisibility() == View.VISIBLE);
}
protected void setProgressBarVisible(boolean visible) { protected void setProgressBarVisible(boolean visible) {
if (mProgressHeader != null) { if (mProgressHeader != null) {
mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -538,7 +481,6 @@ public class NetworkSelectSettings extends DashboardFragment {
} }
private void addMessagePreference(int messageId) { private void addMessagePreference(int messageId) {
setProgressBarVisible(false);
mStatusMessagePreference.setTitle(messageId); mStatusMessagePreference.setTitle(messageId);
mPreferenceCategory.removeAll(); mPreferenceCategory.removeAll();
mPreferenceCategory.addPreference(mStatusMessagePreference); mPreferenceCategory.addPreference(mStatusMessagePreference);

View File

@@ -36,6 +36,8 @@ import kotlinx.coroutines.flow.onEach
private const val TAG = "SubscriptionRepository" private const val TAG = "SubscriptionRepository"
class SubscriptionRepository(private val context: Context) { class SubscriptionRepository(private val context: Context) {
private val subscriptionManager = context.requireSubscriptionManager()
/** /**
* Return a list of subscriptions that are available and visible to the user. * Return a list of subscriptions that are available and visible to the user.
* *
@@ -55,6 +57,7 @@ class SubscriptionRepository(private val context: Context) {
isSubscriptionEnabledFlow(subId).collectLatestWithLifecycle(lifecycleOwner, action = action) isSubscriptionEnabledFlow(subId).collectLatestWithLifecycle(lifecycleOwner, action = action)
} }
fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()
} }
val Context.subscriptionManager: SubscriptionManager? val Context.subscriptionManager: SubscriptionManager?

View File

@@ -29,10 +29,12 @@ import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
class TelephonyRepository( class TelephonyRepository(
private val context: Context, private val context: Context,
@@ -64,19 +66,21 @@ class TelephonyRepository(
telephonyManager.setMobileDataPolicyEnabled(policy, enabled) telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
} }
fun isDataEnabled( fun isDataEnabledFlow(subId: Int): Flow<Boolean> {
subId: Int,
): Flow<Boolean> {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
Log.d(TAG, "register mobileDataEnabledFlow: [$subId]")
return context.mobileDataEnabledFlow(subId) return context.mobileDataEnabledFlow(subId)
.map { .map {
Log.d(TAG, "mobileDataEnabledFlow: receive mobile data [$subId] start")
val telephonyManager = context.telephonyManager(subId) val telephonyManager = context.telephonyManager(subId)
telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
.also { Log.d(TAG, "mobileDataEnabledFlow: [$subId] isDataEnabled(): $it") }
} }
.catch {
Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it)
emit(false)
}
.onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") }
.conflate()
.flowOn(Dispatchers.Default)
} }
fun setMobileData( fun setMobileData(
@@ -100,6 +104,7 @@ class TelephonyRepository(
wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled) wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
} }
} }
private companion object { private companion object {
private const val TAG = "TelephonyRepository" private const val TAG = "TelephonyRepository"
} }

View File

@@ -27,25 +27,29 @@ import android.telephony.TelephonyScanManager
import android.util.Log import android.util.Log
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.telephony.CellInfoUtil
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
import com.android.settings.network.telephony.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
class NetworkScanRepository(private val context: Context, subId: Int) { class NetworkScanRepository(private val context: Context, subId: Int) {
sealed interface NetworkScanResult enum class NetworkScanState {
ACTIVE, COMPLETE, ERROR
}
data class NetworkScanCellInfos(val cellInfos: List<CellInfo>) : NetworkScanResult data class NetworkScanResult(
data object NetworkScanComplete : NetworkScanResult val state: NetworkScanState,
data class NetworkScanError(val error: Int) : NetworkScanResult val cellInfos: List<CellInfo>,
)
private val telephonyManager = private val telephonyManager = context.telephonyManager(subId)
context.getSystemService(TelephonyManager::class.java)!!.createForSubscriptionId(subId)
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */ /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
fun launchNetworkScan(lifecycleOwner: LifecycleOwner, onResult: (NetworkScanResult) -> Unit) { fun launchNetworkScan(lifecycleOwner: LifecycleOwner, onResult: (NetworkScanResult) -> Unit) {
@@ -65,23 +69,29 @@ class NetworkScanRepository(private val context: Context, subId: Int) {
} }
fun networkScanFlow(): Flow<NetworkScanResult> = callbackFlow { fun networkScanFlow(): Flow<NetworkScanResult> = callbackFlow {
var state = NetworkScanState.ACTIVE
var cellInfos: List<CellInfo> = emptyList()
val callback = object : TelephonyScanManager.NetworkScanCallback() { val callback = object : TelephonyScanManager.NetworkScanCallback() {
override fun onResults(results: List<CellInfo>) { override fun onResults(results: List<CellInfo>) {
val cellInfos = results.distinctBy { CellInfoScanKey(it) } cellInfos = results.distinctBy { CellInfoScanKey(it) }
trySend(NetworkScanCellInfos(cellInfos)) sendResult()
Log.d(TAG, "CellInfoList: ${CellInfoUtil.cellInfoListToString(cellInfos)}")
} }
override fun onComplete() { override fun onComplete() {
trySend(NetworkScanComplete) state = NetworkScanState.COMPLETE
close() sendResult()
Log.d(TAG, "onComplete") // Don't call close() here since onComplete() could happens before onResults()
} }
override fun onError(error: Int) { override fun onError(error: Int) {
trySend(NetworkScanError(error)) state = NetworkScanState.ERROR
sendResult()
close() close()
Log.d(TAG, "onError: $error") }
private fun sendResult() {
trySend(NetworkScanResult(state, cellInfos))
} }
} }
@@ -92,7 +102,7 @@ class NetworkScanRepository(private val context: Context, subId: Int) {
) )
awaitClose { networkScan.stopScan() } awaitClose { networkScan.stopScan() }
}.flowOn(Dispatchers.Default) }.conflate().onEach { Log.d(TAG, "networkScanFlow: $it") }.flowOn(Dispatchers.Default)
/** Create network scan for allowed network types. */ /** Create network scan for allowed network types. */
private fun createNetworkScan(): NetworkScanRequest { private fun createNetworkScan(): NetworkScanRequest {

View File

@@ -28,7 +28,10 @@ import java.util.List;
/** /**
* Represents the data class needed to create a Settings Panel. See {@link PanelFragment}. * Represents the data class needed to create a Settings Panel. See {@link PanelFragment}.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public interface PanelContent extends Instrumentable { public interface PanelContent extends Instrumentable {
int VIEW_TYPE_SLIDER = 1; int VIEW_TYPE_SLIDER = 1;

View File

@@ -18,7 +18,10 @@ package com.android.settings.panel;
/** /**
* PanelContentCallback provides a callback interface for {@link PanelFragment} to receive * PanelContentCallback provides a callback interface for {@link PanelFragment} to receive
* events from {@link PanelContent}. * events from {@link PanelContent}.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public interface PanelContentCallback { public interface PanelContentCallback {
/** /**

View File

@@ -19,6 +19,7 @@ package com.android.settings.panel;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
@Deprecated(forRemoval = true)
public interface PanelFeatureProvider { public interface PanelFeatureProvider {
/** /**

View File

@@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.flags.Flags; import com.android.settings.flags.Flags;
@Deprecated(forRemoval = true)
public class PanelFeatureProviderImpl implements PanelFeatureProvider { public class PanelFeatureProviderImpl implements PanelFeatureProvider {
@Override @Override

View File

@@ -66,6 +66,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Deprecated(forRemoval = true)
public class PanelFragment extends Fragment { public class PanelFragment extends Fragment {
private static final String TAG = "PanelFragment"; private static final String TAG = "PanelFragment";
@@ -519,6 +520,7 @@ public class PanelFragment extends Fragment {
return mPanel.getViewType(); return mPanel.getViewType();
} }
@Deprecated(forRemoval = true)
class LocalPanelCallback implements PanelContentCallback { class LocalPanelCallback implements PanelContentCallback {
@Override @Override

View File

@@ -21,7 +21,10 @@ package com.android.settings.panel;
* <p> * <p>
* Constants should only be removed if underlying panel, or use case is removed. * Constants should only be removed if underlying panel, or use case is removed.
* </p> * </p>
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class PanelLoggingContract { public class PanelLoggingContract {
/** /**

View File

@@ -48,7 +48,10 @@ import java.util.Map;
/** /**
* RecyclerView adapter for Slices in Settings Panels. * RecyclerView adapter for Slices in Settings Panels.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class PanelSlicesAdapter public class PanelSlicesAdapter
extends RecyclerView.Adapter<PanelSlicesAdapter.SliceRowViewHolder> { extends RecyclerView.Adapter<PanelSlicesAdapter.SliceRowViewHolder> {
@@ -112,7 +115,10 @@ public class PanelSlicesAdapter
/** /**
* ViewHolder for binding Slices to SliceViews. * ViewHolder for binding Slices to SliceViews.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class SliceRowViewHolder extends RecyclerView.ViewHolder public class SliceRowViewHolder extends RecyclerView.ViewHolder
implements DividerItemDecoration.DividedViewHolder { implements DividerItemDecoration.DividedViewHolder {

View File

@@ -36,7 +36,10 @@ import java.util.concurrent.CountDownLatch;
* {@link Uri}. Then check if all of the Slices have loaded with * {@link Uri}. Then check if all of the Slices have loaded with
* {@link #isPanelReadyToLoad()}, which will return {@code true} the first time after all * {@link #isPanelReadyToLoad()}, which will return {@code true} the first time after all
* Slices have loaded. * Slices have loaded.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class PanelSlicesLoaderCountdownLatch { public class PanelSlicesLoaderCountdownLatch {
private final Set<Uri> mLoadedSlices; private final Set<Uri> mLoadedSlices;
private final CountDownLatch mCountDownLatch; private final CountDownLatch mCountDownLatch;

View File

@@ -42,7 +42,10 @@ import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
/** /**
* Dialog Activity to host Settings Slices. * Dialog Activity to host Settings Slices.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class SettingsPanelActivity extends FragmentActivity { public class SettingsPanelActivity extends FragmentActivity {
private static final String TAG = "SettingsPanelActivity"; private static final String TAG = "SettingsPanelActivity";

View File

@@ -19,6 +19,7 @@ package com.android.settings.privatespace;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD; import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS; import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -36,11 +37,12 @@ import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.SetScreenLockDialogActivity; import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.transition.SettingsTransitionHelper; import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -52,7 +54,7 @@ import com.google.android.setupdesign.util.ThemeHelper;
* user to set a device lock if not set with an alert dialog. This can be launched using the intent * user to set a device lock if not set with an alert dialog. This can be launched using the intent
* com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS. * com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS.
*/ */
public class PrivateSpaceAuthenticationActivity extends SettingsActivity { public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
private static final String TAG = "PrivateSpaceAuthCheck"; private static final String TAG = "PrivateSpaceAuthCheck";
public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED = public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED =
"extra_show_private_space_unlocked"; "extra_show_private_space_unlocked";
@@ -76,31 +78,55 @@ public class PrivateSpaceAuthenticationActivity extends SettingsActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (isFinishing()) { if (!(Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures())) {
finish();
return; return;
} }
if (Flags.allowPrivateProfile() Intent intent = getIntent();
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) { String highlightMenuKey = getString(R.string.menu_key_security);
ThemeHelper.trySetDynamicColor(this); if (shouldShowMultiPaneDeepLink(intent)
mPrivateSpaceMaintainer = && tryStartMultiPaneDeepLink(this, intent, highlightMenuKey)) {
new Injector().injectPrivateSpaceMaintainer(getApplicationContext()); finish();
if (getKeyguardManager().isDeviceSecure()) { return;
if (savedInstanceState == null) { }
if (mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
unlockAndLaunchPrivateSpaceSettings(this); ThemeHelper.trySetDynamicColor(this);
} else { mPrivateSpaceMaintainer =
authenticatePrivateSpaceEntry(); new Injector().injectPrivateSpaceMaintainer(getApplicationContext());
} if (getKeyguardManager().isDeviceSecure()) {
if (savedInstanceState == null) {
if (mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
unlockAndLaunchPrivateSpaceSettings(this);
} else {
authenticatePrivateSpaceEntry();
} }
} else {
promptToSetDeviceLock();
} }
} else { } else {
finish(); promptToSetDeviceLock();
} }
} }
private boolean shouldShowMultiPaneDeepLink(Intent intent) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
return false;
}
// If the activity is task root, starting trampoline is needed in order to show two-pane UI.
// If FLAG_ACTIVITY_NEW_TASK is set, the activity will become the start of a new task on
// this history stack, so starting trampoline is needed in order to notify the homepage that
// the highlight key is changed.
if (!isTaskRoot() && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
return false;
}
// Only starts trampoline for deep links. Should return false for all the cases that
// Settings app starts SettingsActivity or SubSetting by itself.
// Other apps should send deep link intent which matches intent filter of the Activity.
return intent.getAction() != null;
}
/** Starts private space setup flow or the PS settings page on device lock authentication */ /** Starts private space setup flow or the PS settings page on device lock authentication */
@VisibleForTesting @VisibleForTesting
public void onLockAuthentication(Context context) { public void onLockAuthentication(Context context) {

View File

@@ -36,11 +36,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
@@ -62,7 +62,6 @@ import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBool
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@@ -207,7 +206,7 @@ fun MobileDataSectionImpl(
}.collectAsStateWithLifecycle(initialValue = null) }.collectAsStateWithLifecycle(initialValue = null)
val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) { val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
TelephonyRepository(context).isDataEnabled(mobileDataSelectedId.intValue) TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue)
}.collectAsStateWithLifecycle(initialValue = false) }.collectAsStateWithLifecycle(initialValue = false)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()

View File

@@ -33,14 +33,12 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment; import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import com.android.settings.development.DeveloperOptionAwareMixin;
import com.android.settingslib.applications.DefaultAppInfo; import com.android.settingslib.applications.DefaultAppInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class WebViewAppPicker extends DefaultAppPickerFragment implements public class WebViewAppPicker extends DefaultAppPickerFragment {
DeveloperOptionAwareMixin {
private WebViewUpdateServiceWrapper mWebViewUpdateServiceWrapper; private WebViewUpdateServiceWrapper mWebViewUpdateServiceWrapper;
private WebViewUpdateServiceWrapper getWebViewUpdateServiceWrapper() { private WebViewUpdateServiceWrapper getWebViewUpdateServiceWrapper() {

View File

@@ -34,6 +34,7 @@ import androidx.window.embedding.ActivityEmbeddingController;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.flags.Flags;
import com.android.settings.homepage.SettingsHomepageActivity; import com.android.settings.homepage.SettingsHomepageActivity;
/** /**
@@ -46,9 +47,13 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L; static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L;
private static final int RES_NORMAL_BACKGROUND = private static final int RES_NORMAL_BACKGROUND =
R.drawable.homepage_selectable_item_background; Flags.homepageRevamp()
? R.drawable.homepage_selectable_item_background_v2
: R.drawable.homepage_selectable_item_background;
private static final int RES_HIGHLIGHTED_BACKGROUND = private static final int RES_HIGHLIGHTED_BACKGROUND =
R.drawable.homepage_highlighted_item_background; Flags.homepageRevamp()
? R.drawable.homepage_highlighted_item_background_v2
: R.drawable.homepage_highlighted_item_background;
private final int mTitleColorNormal; private final int mTitleColorNormal;
private final int mTitleColorHighlight; private final int mTitleColorHighlight;

View File

@@ -22,6 +22,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.flags.Flags;
/** Helper for homepage preference to manage layout. */ /** Helper for homepage preference to manage layout. */
public class HomepagePreferenceLayoutHelper { public class HomepagePreferenceLayoutHelper {
@@ -39,7 +40,10 @@ public class HomepagePreferenceLayoutHelper {
} }
public HomepagePreferenceLayoutHelper(Preference preference) { public HomepagePreferenceLayoutHelper(Preference preference) {
preference.setLayoutResource(R.layout.homepage_preference); preference.setLayoutResource(
Flags.homepageRevamp()
? R.layout.homepage_preference_v2
: R.layout.homepage_preference);
} }
/** Sets whether the icon should be visible */ /** Sets whether the icon should be visible */

View File

@@ -30,7 +30,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.AudioManager; import android.media.AudioManager;
@@ -44,7 +44,6 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.shadow.ShadowAudioManager; import com.android.settings.testutils.shadow.ShadowAudioManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager; import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.flags.Flags; import com.android.settingslib.flags.Flags;
@@ -68,7 +67,7 @@ import java.util.Collection;
public class ConnectedBluetoothDeviceUpdaterTest { public class ConnectedBluetoothDeviceUpdaterTest {
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name"; private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
@Rule @Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -355,13 +354,16 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test @Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_notAllowedExclusiveManagedDevice_addDevice() { public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
throws Exception {
mAudioManager.setMode(AudioManager.MODE_NORMAL); mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
FAKE_EXCLUSIVE_MANAGER_NAME.getBytes()); TEST_EXCLUSIVE_MANAGER.getBytes());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
@@ -370,64 +372,39 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test @Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference() public void update_exclusivelyManagedDevice_packageNotEnabled_addDevice()
throws Exception { throws Exception {
final String exclusiveManagerName = ApplicationInfo appInfo = new ApplicationInfo();
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse( appInfo.enabled = false;
FAKE_EXCLUSIVE_MANAGER_NAME);
mAudioManager.setMode(AudioManager.MODE_NORMAL); mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes()); TEST_EXCLUSIVE_MANAGER.getBytes());
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0); doReturn(appInfo).when(mPackageManager).getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference()
throws Exception {
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes());
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice()
throws Exception {
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
exclusiveManagerName, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
} }
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()
throws Exception {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
TEST_EXCLUSIVE_MANAGER.getBytes());
doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
}
} }

View File

@@ -29,7 +29,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsDisabled;
@@ -41,7 +41,6 @@ import android.util.Pair;
import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -66,7 +65,7 @@ import java.util.List;
public class SavedBluetoothDeviceUpdaterTest { public class SavedBluetoothDeviceUpdaterTest {
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name"; private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
@Rule @Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -339,42 +338,18 @@ public class SavedBluetoothDeviceUpdaterTest {
@Test @Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_notAllowedExclusivelyManagedDevice_addDevice() { public void update_existingExclusivelyManagedDevice_packageEnabled_removePreference()
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedBluetoothDevice);
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference()
throws Exception { throws Exception {
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>(); final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
when(mBluetoothAdapter.isEnabled()).thenReturn(true); when(mBluetoothAdapter.isEnabled()).thenReturn(true);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices); when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false); when(mBluetoothDevice.isConnected()).thenReturn(false);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes()); TEST_EXCLUSIVE_MANAGER.getBytes());
doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0); TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference); mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
@@ -386,23 +361,19 @@ public class SavedBluetoothDeviceUpdaterTest {
@Test @Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference() public void update_newExclusivelyManagedDevice_packageEnabled_doNotAddPreference()
throws Exception { throws Exception {
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>(); final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
cachedDevices.add(mCachedBluetoothDevice); cachedDevices.add(mCachedBluetoothDevice);
when(mBluetoothAdapter.isEnabled()).thenReturn(true); when(mBluetoothAdapter.isEnabled()).thenReturn(true);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices); when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false); when(mBluetoothDevice.isConnected()).thenReturn(false);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes()); TEST_EXCLUSIVE_MANAGER.getBytes());
doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0); TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
@@ -413,24 +384,42 @@ public class SavedBluetoothDeviceUpdaterTest {
@Test @Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice() public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
throws Exception { throws Exception {
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>(); final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
cachedDevices.add(mCachedBluetoothDevice); cachedDevices.add(mCachedBluetoothDevice);
when(mBluetoothAdapter.isEnabled()).thenReturn(true); when(mBluetoothAdapter.isEnabled()).thenReturn(true);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices); when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false); when(mBluetoothDevice.isConnected()).thenReturn(false);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes()); TEST_EXCLUSIVE_MANAGER.getBytes());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo( mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
exclusiveManagerName, 0);
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageNotEnabled_addDevice()
throws Exception {
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedBluetoothDevice);
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.enabled = false;
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
TEST_EXCLUSIVE_MANAGER.getBytes());
doReturn(appInfo).when(mPackageManager).getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -28,6 +28,7 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accessibility.ShortcutsSettingsFragment;
import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.XmlTestUtils;
import org.junit.Before; import org.junit.Before;

View File

@@ -14,15 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.accessibility.Flags;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -31,19 +38,30 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class ContrastPreferenceControllerTest { public class ContrastPreferenceControllerTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREFERENCE_KEY = "preference_key"; private static final String PREFERENCE_KEY = "preference_key";
private Context mContext;
private ContrastPreferenceController mController; private ContrastPreferenceController mController;
@Before @Before
public void setUp() { public void setUp() {
mController = new ContrastPreferenceController(ApplicationProvider.getApplicationContext(), mContext = ApplicationProvider.getApplicationContext();
PREFERENCE_KEY); mController = new ContrastPreferenceController(mContext, PREFERENCE_KEY);
} }
@Test @Test
public void getAvailabilityStatus_shouldReturnUnavailable() { @EnableFlags(Flags.FLAG_ENABLE_COLOR_CONTRAST_CONTROL)
public void getAvailabilityStatus_flagsEnabled_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()) assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE); .isEqualTo(BasePreferenceController.AVAILABLE);
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_COLOR_CONTRAST_CONTROL)
public void getAvailabilityStatus_flagsDisabled_shouldReturnUnsupported() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
} }
} }

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.accessibility; package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;

View File

@@ -50,6 +50,7 @@ import android.widget.TextView;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -58,6 +59,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@@ -84,10 +86,13 @@ public final class BatteryChartPreferenceControllerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
Locale.setDefault(new Locale("en_US")); Locale.setDefault(new Locale("en_US"));
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false); org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
TimeZone.setDefault(TimeZone.getTimeZone("UTC")); final TimeZone timeZone = TimeZone.getTimeZone("UTC");
TimeZone.setDefault(timeZone);
DataProcessor.sTestSystemAppsPackageNames = Set.of(); DataProcessor.sTestSystemAppsPackageNames = Set.of();
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
BatteryLevelData.sTestCalendar = Calendar.getInstance();
BatteryLevelData.sTestCalendar.setTimeZone(timeZone);
doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(true).when(mUserManager).isUserUnlocked(anyInt()); doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
@@ -115,6 +120,11 @@ public final class BatteryChartPreferenceControllerTest {
new BatteryEntry.NameAndIcon("fakeName", /* icon= */ null, /* iconId= */ 1)); new BatteryEntry.NameAndIcon("fakeName", /* icon= */ null, /* iconId= */ 1));
} }
@After
public void tearDown() {
BatteryLevelData.sTestCalendar = null;
}
@Test @Test
public void onDestroy_activityIsChanging_clearBatteryEntryCache() { public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
doReturn(true).when(mSettingsActivity).isChangingConfigurations(); doReturn(true).when(mSettingsActivity).isChangingConfigurations();
@@ -141,7 +151,8 @@ public final class BatteryChartPreferenceControllerTest {
reset(mHourlyChartView); reset(mHourlyChartView);
setupHourlyChartViewAnimationMock(); setupHourlyChartViewAnimationMock();
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE); verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
// Ignore fast refresh ui from the data processor callback. // Ignore fast refresh ui from the data processor callback.
@@ -178,7 +189,8 @@ public final class BatteryChartPreferenceControllerTest {
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
mBatteryChartPreferenceController.mDailyChartLabelTextGenerator); mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE); verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f); verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f);
@@ -283,7 +295,8 @@ public final class BatteryChartPreferenceControllerTest {
public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() { public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() {
doReturn(View.GONE).when(mHourlyChartView).getVisibility(); doReturn(View.GONE).when(mHourlyChartView).getVisibility();
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
verify(mDailyChartView).setVisibility(View.GONE); verify(mDailyChartView).setVisibility(View.GONE);
@@ -295,7 +308,8 @@ public final class BatteryChartPreferenceControllerTest {
doReturn(View.GONE).when(mHourlyChartView).getVisibility(); doReturn(View.GONE).when(mHourlyChartView).getVisibility();
mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
verify(mDailyChartView).setVisibility(View.VISIBLE); verify(mDailyChartView).setVisibility(View.VISIBLE);
@@ -307,7 +321,8 @@ public final class BatteryChartPreferenceControllerTest {
doReturn(View.GONE).when(mHourlyChartView).getVisibility(); doReturn(View.GONE).when(mHourlyChartView).getVisibility();
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
verify(mDailyChartView).setVisibility(View.VISIBLE); verify(mDailyChartView).setVisibility(View.VISIBLE);
@@ -379,7 +394,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_selectAllDaysAllHours_returnNull() { public void selectedSlotText_selectAllDaysAllHours_returnNull() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
@@ -390,7 +406,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() { public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
@@ -401,7 +418,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_selectADayAllHours_onlyDayText() { public void selectedSlotText_selectADayAllHours_onlyDayText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 1; mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
@@ -412,7 +430,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() { public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 2; mBatteryChartPreferenceController.mHourlyChartIndex = 2;
@@ -426,7 +445,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_SelectADayAnHour_dayAndHourText() { public void selectedSlotText_SelectADayAnHour_dayAndHourText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 1; mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = 8; mBatteryChartPreferenceController.mHourlyChartIndex = 8;
@@ -439,8 +459,9 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void selectedSlotText_selectFirstSlot_withMinuteText() { public void selectedSlotText_selectFirstSlotAfterFullCharged_withMinuteText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 0;
@@ -452,9 +473,29 @@ public final class BatteryChartPreferenceControllerTest {
.isEqualTo("Battery level percentage from 100% to 99%"); .isEqualTo("Battery level percentage from 100% to 99%");
} }
@Test
public void selectedSlotText_selectFirstSlotAfterTimeUpdated_withMinuteText() {
BatteryLevelData batteryLevelData =
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10);
assertThat(batteryLevelData.getHourlyBatteryLevelsPerDay().get(0).isStartTimestamp())
.isTrue();
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0;
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("7:01 AM - 8 AM");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("7:01 AM to 8 AM");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 90% to 89%");
}
@Test @Test
public void selectedSlotText_selectLastSlot_withNowText() { public void selectedSlotText_selectLastSlot_withNowText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 3; mBatteryChartPreferenceController.mHourlyChartIndex = 3;
@@ -468,7 +509,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() { public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(1)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 1, /* levelOffset= */ 0));
mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 0;
@@ -493,7 +535,8 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mHourlyChartIndex = -1; mBatteryChartPreferenceController.mHourlyChartIndex = -1;
mBatteryChartPreferenceController.onCreate(bundle); mBatteryChartPreferenceController.onCreate(bundle);
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(25)); mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
createBatteryLevelData(/* numOfHours= */ 25, /* levelOffset= */ 0));
assertThat(mBatteryChartPreferenceController.mDailyChartIndex) assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
.isEqualTo(expectedDailyIndex); .isEqualTo(expectedDailyIndex);
@@ -503,7 +546,8 @@ public final class BatteryChartPreferenceControllerTest {
@Test @Test
public void getTotalHours_getExpectedResult() { public void getTotalHours_getExpectedResult() {
BatteryLevelData batteryLevelData = createBatteryLevelData(60); BatteryLevelData batteryLevelData =
createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0);
final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData); final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData);
@@ -516,10 +560,10 @@ public final class BatteryChartPreferenceControllerTest {
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
} }
private static BatteryLevelData createBatteryLevelData(int numOfHours) { private static BatteryLevelData createBatteryLevelData(int numOfHours, int levelOffset) {
Map<Long, Integer> batteryLevelMap = new ArrayMap<>(); Map<Long, Integer> batteryLevelMap = new ArrayMap<>();
for (int index = 0; index < numOfHours; index += 2) { for (int index = 0; index < numOfHours; index += 2) {
final Integer level = 100 - index; final Integer level = 100 - index - levelOffset;
Long timestamp = generateTimestamp(index); Long timestamp = generateTimestamp(index);
if (index == 0) { if (index == 0) {
timestamp += DateUtils.MINUTE_IN_MILLIS; timestamp += DateUtils.MINUTE_IN_MILLIS;
@@ -529,6 +573,8 @@ public final class BatteryChartPreferenceControllerTest {
} }
long current = generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2; long current = generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2;
batteryLevelMap.put(current, 66); batteryLevelMap.put(current, 66);
BatteryLevelData.sTestCalendar.setTimeInMillis(current);
DataProcessor.sTestCurrentTimeMillis = current; DataProcessor.sTestCurrentTimeMillis = current;
return new BatteryLevelData(batteryLevelMap); return new BatteryLevelData(batteryLevelMap);
} }

View File

@@ -35,7 +35,6 @@ import com.android.settings.testutils.BatteryTestUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -64,9 +63,8 @@ public final class BootBroadcastReceiverTest {
// Inserts fake data into database for testing. // Inserts fake data into database for testing.
final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext); final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis(), "com.android.systemui");
mDao = database.batteryStateDao(); mDao = database.batteryStateDao();
mDao.clearAll();
clearSharedPreferences(); clearSharedPreferences();
} }
@@ -129,10 +127,13 @@ public final class BootBroadcastReceiverTest {
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull(); assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
} }
@Ignore("b/314921894")
@Test @Test
public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob() public void onReceive_withTimeChangedIntentSetEarlierTime_refreshesJob()
throws InterruptedException { throws InterruptedException {
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED)); mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
TimeUnit.MILLISECONDS.sleep(100); TimeUnit.MILLISECONDS.sleep(100);
@@ -140,6 +141,52 @@ public final class BootBroadcastReceiverTest {
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull(); assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
} }
@Test
public void onReceive_withTimeChangedIntentSetLaterTime_clearNoDataAndRefreshesJob()
throws InterruptedException {
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis() - 60000, "com.android.systemui");
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
TimeUnit.MILLISECONDS.sleep(100);
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
}
@Test
public void onReceive_withTimeFormatChangedIntent_skipRefreshJob() throws InterruptedException {
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
mReceiver.onReceive(
mContext,
new Intent(Intent.EXTRA_INTENT)
.putExtra(
Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR));
TimeUnit.MILLISECONDS.sleep(100);
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
}
@Test
public void onReceive_withTimeZoneChangedIntent_clearAllDataAndRefreshesJob()
throws InterruptedException {
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis(), "com.android.systemui");
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIMEZONE_CHANGED));
TimeUnit.MILLISECONDS.sleep(100);
assertThat(mDao.getAllAfter(0)).isEmpty();
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
}
@Test @Test
public void invokeJobRecheck_broadcastsIntent() { public void invokeJobRecheck_broadcastsIntent() {
BootBroadcastReceiver.invokeJobRecheck(mContext); BootBroadcastReceiver.invokeJobRecheck(mContext);

View File

@@ -170,7 +170,8 @@ public final class DataProcessManagerTest {
final Map<Long, Integer> batteryLevelMap1 = final Map<Long, Integer> batteryLevelMap1 =
Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100); Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1)); new BatteryLevelData.PeriodBatteryLevelData(
batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false));
// Adds the day 2 data. // Adds the day 2 data.
hourlyBatteryLevelsPerDay.add(null); hourlyBatteryLevelsPerDay.add(null);
// Adds the day 3 data. // Adds the day 3 data.
@@ -178,7 +179,8 @@ public final class DataProcessManagerTest {
final Map<Long, Integer> batteryLevelMap2 = final Map<Long, Integer> batteryLevelMap2 =
Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100); Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2)); new BatteryLevelData.PeriodBatteryLevelData(
batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false));
// Fake current usage data. // Fake current usage data.
final UsageEvents.Event event1 = final UsageEvents.Event event1 =
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /* timestamp= */ 1, packageName); getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /* timestamp= */ 1, packageName);

View File

@@ -209,7 +209,8 @@ public final class DataProcessorTest {
final Map<Long, Integer> batteryLevelMap1 = final Map<Long, Integer> batteryLevelMap1 =
Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100); Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1)); new BatteryLevelData.PeriodBatteryLevelData(
batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false));
// Adds the day 2 data. // Adds the day 2 data.
hourlyBatteryLevelsPerDay.add(null); hourlyBatteryLevelsPerDay.add(null);
// Adds the day 3 data. // Adds the day 3 data.
@@ -217,7 +218,8 @@ public final class DataProcessorTest {
final Map<Long, Integer> batteryLevelMap2 = final Map<Long, Integer> batteryLevelMap2 =
Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100); Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2)); new BatteryLevelData.PeriodBatteryLevelData(
batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false));
final List<AppUsageEvent> appUsageEventList = new ArrayList<>(); final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
// Adds some events before the start timestamp. // Adds some events before the start timestamp.
appUsageEventList.add( appUsageEventList.add(
@@ -365,7 +367,8 @@ public final class DataProcessorTest {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>(); new ArrayList<>();
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>())); new BatteryLevelData.PeriodBatteryLevelData(
new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false));
assertThat( assertThat(
DataProcessor.generateAppUsagePeriodMap( DataProcessor.generateAppUsagePeriodMap(
mContext, mContext,
@@ -858,7 +861,8 @@ public final class DataProcessorTest {
new ArrayList<>(); new ArrayList<>();
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>())); new BatteryLevelData.PeriodBatteryLevelData(
new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false));
assertThat( assertThat(
DataProcessor.getBatteryDiffDataMap( DataProcessor.getBatteryDiffDataMap(

View File

@@ -16,8 +16,6 @@
package com.android.settings.homepage; package com.android.settings.homepage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@@ -59,11 +57,6 @@ public class TopLevelSettingsTest {
mSettings.onAttach(mContext); mSettings.onAttach(mContext);
} }
@Test
public void shouldForceRoundedIcon_true() {
assertThat(mSettings.shouldForceRoundedIcon()).isTrue();
}
@Test @Test
public void onCreatePreferences_shouldTintPreferenceIcon() { public void onCreatePreferences_shouldTintPreferenceIcon() {
final Preference preference = new Preference(mContext); final Preference preference = new Preference(mContext);

View File

@@ -29,7 +29,10 @@ import java.util.List;
/** /**
* Fake PanelContent for testing. * Fake PanelContent for testing.
*
* @deprecated this is no longer used after V and will be removed.
*/ */
@Deprecated(forRemoval = true)
public class FakePanelContent implements PanelContent { public class FakePanelContent implements PanelContent {
public static final String FAKE_ACTION = "fake_action"; public static final String FAKE_ACTION = "fake_action";

View File

@@ -19,6 +19,7 @@ package com.android.settings.panel;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
@Deprecated(forRemoval = true)
public class FakeSettingsPanelActivity extends SettingsPanelActivity { public class FakeSettingsPanelActivity extends SettingsPanelActivity {
@Override @Override
public ComponentName getCallingActivity() { public ComponentName getCallingActivity() {

View File

@@ -54,6 +54,7 @@ import org.robolectric.annotation.Config;
import java.util.Objects; import java.util.Objects;
@Deprecated(forRemoval = true)
@Ignore("b/313576125") @Ignore("b/313576125")
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = { @Config(shadows = {

Some files were not shown because too many files have changed in this diff Show More