diff options
364 files changed, 14175 insertions, 800 deletions
diff --git a/Android.bp b/Android.bp index dce25d7dda..780741654e 100644 --- a/Android.bp +++ b/Android.bp @@ -35,6 +35,27 @@ java_library { ], } +soong_config_module_type_import { + from: "device/qcom/qssi/Android.bp", + module_types: [ + "bredr_vs_btadva_java_defaults", + ], +} + +bredr_vs_btadva_java_defaults { + name: "btadva_settings_java_defaults", + + soong_config_variables: { + bredr_or_btadva: { + btadva: { + srcs: [ + ":settings-bluetooth-adva-srcs", + ], + } + } + } +} + // Build the Settings APK android_library { name: "Settings-core", @@ -42,6 +63,8 @@ android_library { defaults: [ "SettingsLibDefaults", "SettingsLib-search-defaults", + "framework-wifi-vendor-hide-access-defaults", + "btadva_settings_java_defaults", ], srcs: ["src/**/*.java"], @@ -80,6 +103,8 @@ android_library { "telephony-common", "ims-common", "app-compat-annotations", + "telephony-ext", + "extphonelib", ], } @@ -110,6 +135,9 @@ android_app { optimize: { proguard_flags_files: ["proguard.flags"], }, + libs: [ + "extphonelib" + ], } android_library_import { diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c84132fd43..88476002a7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -109,6 +109,7 @@ <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" /> <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> <uses-permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS" /> + <uses-permission android:name="com.qualcomm.qti.permission.USE_EXT_TELEPHONY_SERVICE" /> <application android:name=".SettingsApplication" @@ -125,6 +126,7 @@ android:appComponentFactory="androidx.core.app.CoreComponentFactory"> <uses-library android:name="org.apache.http.legacy"/> + <uses-library android:name="com.qti.extphone.extphonelib"/> <uses-library android:name="androidx.window.extensions" android:required="false"/> <uses-library android:name="androidx.window.sidecar" android:required="false"/> @@ -221,7 +223,6 @@ <intent-filter android:priority="1"> <action android:name="android.settings.WIRELESS_SETTINGS" /> <action android:name="android.settings.AIRPLANE_MODE_SETTINGS" /> - <action android:name="com.android.settings.sim.SIM_SUB_INFO_SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <intent-filter> @@ -1151,6 +1152,37 @@ android:value="true" /> </activity> + <!-- Keep compatibility with old shortcuts. --> + <activity-alias android:name="DisplaySettings" + android:label="@string/display_settings" + android:exported="true" + android:targetActivity="Settings$DisplaySettingsActivity"> + <meta-data android:name="com.android.settings.FRAGMENT_CLASS" + android:value="com.android.settings.DisplaySettings" /> + </activity-alias> + <activity android:name="Settings$SMQQtiFeedbackActivity" + android:exported="true" + android:label="@string/qtifeedback_settings_title" + android:icon="@drawable/ic_settings_qti_feedback" + android:enabled="false" + android:taskAffinity=""> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <intent-filter android:priority="250"> + <action android:name="com.android.settings.action.SETTINGS" /> + </intent-filter> + <meta-data android:name="com.android.settings.category" + android:value="com.android.settings.category.ia.system" /> + <meta-data android:name="com.android.settings.FRAGMENT_CLASS" + android:value="@string/qtifeedback_intent_action" /> + <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" + android:value="true" /> + <meta-data android:name="com.android.settings.summary" + android:resource="@string/qtifeedback_settings_subtitle" /> + </activity> + <activity android:name="Settings$SmartAutoRotateSettingsActivity" android:label="@string/accelerometer_title" @@ -4258,6 +4290,24 @@ android:theme="@style/SudThemeGlif.Light"> </activity> + <activity android:name=".network.telephony.UserPLMNListActivity" + android:exported="true" + android:label="@string/uplmn_settings_title" + android:configChanges="orientation|screenSize|keyboardHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + + <activity android:name=".network.telephony.UserPLMNEditorActivity" + android:exported="true" + android:label="@string/uplmn_settings_title" + android:configChanges="orientation|screenSize|keyboardHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest> @@ -10,6 +10,54 @@ See the License for the specific language governing permissions and limitations under the License. +________________________________________ + +Copyright (c) 2018, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +______________________________________ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved + * Not a contribution + + * 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. + */ +______________________________________ Apache License Version 2.0, January 2004 diff --git a/proguard.flags b/proguard.flags index 3ed713636b..45294c8fff 100644 --- a/proguard.flags +++ b/proguard.flags @@ -67,6 +67,11 @@ public <init>(android.content.Context, android.net.Uri); } +# Keep ImsConnector members for FeatureConnector.Listener<ImsManager> +-keep class com.android.settings.deviceinfo.ImsConnector { + *; +} + # Keep WM Jetpack classes and callbacks -keep class androidx.window.** { *; } -dontwarn androidx.window.extensions.** diff --git a/res/drawable-mdpi/ic_settings_qti_feedback.png b/res/drawable-mdpi/ic_settings_qti_feedback.png Binary files differnew file mode 100644 index 0000000000..e7c0dae3c3 --- /dev/null +++ b/res/drawable-mdpi/ic_settings_qti_feedback.png diff --git a/res/drawable-xhdpi/ic_settings_qti_feedback.png b/res/drawable-xhdpi/ic_settings_qti_feedback.png Binary files differnew file mode 100644 index 0000000000..a20f94e91f --- /dev/null +++ b/res/drawable-xhdpi/ic_settings_qti_feedback.png diff --git a/res/drawable-xxhdpi/ic_settings_qti_feedback.png b/res/drawable-xxhdpi/ic_settings_qti_feedback.png Binary files differnew file mode 100644 index 0000000000..5d0ccf6a43 --- /dev/null +++ b/res/drawable-xxhdpi/ic_settings_qti_feedback.png diff --git a/res/drawable-xxxhdpi/ic_settings_qti_feedback.png b/res/drawable-xxxhdpi/ic_settings_qti_feedback.png Binary files differnew file mode 100644 index 0000000000..3dfb55d6c7 --- /dev/null +++ b/res/drawable-xxxhdpi/ic_settings_qti_feedback.png diff --git a/res/drawable/ic_volume_media_bt.xml b/res/drawable/ic_volume_media_bt.xml new file mode 100644 index 0000000000..de0376040c --- /dev/null +++ b/res/drawable/ic_volume_media_bt.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2020, The Linux Foundation. All rights reserved. + Not a contribution +--> + +<!-- + Copyright (C) 2017 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"> + <path + android:fillColor="@android:color/white" + android:pathData="M9 3l0.01 10.55c-0.6-0.34-1.28-0.55-2-0.55C4.79 13 3 14.79 3 17s1.79 4 4.01 4S11 19.21 11 17V7h4V3H9zm12 9.43L17.57 9h-0.6v4.55l-2.75-2.75-0.85 0.85 L16.73 15l-3.35 3.35 0.85 0.85 2.75-2.75V21h0.6L21 17.57 18.42 15 21 12.43zm-2.83-1.13l1.13 1.13-1.13 1.13V11.3zm1.13 6.27l-1.13 1.13v-2.26l1.13 1.13z" /> +</vector> diff --git a/res/layout/bluetooth_audio_codec_dialog.xml b/res/layout/bluetooth_audio_codec_dialog.xml index 9636427055..1fd9de8021 100644 --- a/res/layout/bluetooth_audio_codec_dialog.xml +++ b/res/layout/bluetooth_audio_codec_dialog.xml @@ -52,8 +52,16 @@ layout="@layout/preference_widget_dialog_radiobutton"/> <include + android:id="@+id/bluetooth_audio_codec_aptx_adaptive" + layout="@layout/preference_widget_dialog_radiobutton"/> + + <include android:id="@+id/bluetooth_audio_codec_ldac" layout="@layout/preference_widget_dialog_radiobutton"/> + + <include + android:id="@+id/bluetooth_audio_codec_aptx_twsp" + layout="@layout/preference_widget_dialog_radiobutton"/> </RadioGroup> <include diff --git a/res/layout/bluetooth_group_options.xml b/res/layout/bluetooth_group_options.xml new file mode 100644 index 0000000000..d518f057ba --- /dev/null +++ b/res/layout/bluetooth_group_options.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:id="@+id/id_tv_groupid" + style="@style/SettingsLibActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + + <TextView + android:text="@string/active" + android:id="@+id/id_tv_status" + style="@style/SettingsLibActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + + <ProgressBar + android:id="@+id/id_progress_group_scan" + style="?android:attr/progressBarStyleSmallTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_gravity="center_vertical" + android:layout_marginStart="16dip" + android:layout_weight="1" + android:minWidth="32dp" + android:text="@string/refresh_group" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/id_btn_connect" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/connect_group" /> + + <Button + android:id="@+id/id_btn_disconnect" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/disconnect_group" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/id_btn_forget" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="forget" /> + + <Button + android:id="@+id/id_btn_refresh" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/refresh_group" /> + + <Button + android:id="@+id/id_btn_refresh_cancel" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/cancel_refresh_group" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/id_btn_group_add_source" + style="@style/GroupOptionsButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/add_source_group" /> + </LinearLayout> +</LinearLayout> diff --git a/res/layout/preference_ba_device_volume_slider.xml b/res/layout/preference_ba_device_volume_slider.xml new file mode 100644 index 0000000000..a7806c1083 --- /dev/null +++ b/res/layout/preference_ba_device_volume_slider.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2020, The Linux Foundation. All rights reserved. + Not a contribution +--> + +<!-- Copyright (C) 2014 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:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:gravity="center_vertical" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:clickable="false" + android:orientation="horizontal"> + + <LinearLayout + android:id="@+id/icon_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="44dp" + android:gravity="start|center_vertical" + android:orientation="horizontal" + android:paddingEnd="12dp" + android:paddingTop="4dp" + android:paddingBottom="4dp"> + <com.android.internal.widget.PreferenceImageView + android:id="@android:id/icon" + android:layout_width="24dp" + android:layout_height="24dp"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="16dp" + android:layout_marginBottom="8dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@android:id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingStart="12dp" + android:singleLine="true" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" + android:textColor="?android:attr/textColorPrimary" + android:ellipsize="marquee" + android:fadingEdge="horizontal"/> + <!-- Preference should place its actual preference widget here. --> + <LinearLayout + android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="end|center_vertical" + android:paddingStart="12dp" + android:orientation="vertical"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <SeekBar + android:id="@*android:id/seekbar" + android:layout_gravity="center_vertical" + android:paddingStart="12dp" + android:layout_width="match_parent" + android:layout_height="48dp"/> + + <TextView + android:id="@+id/suppression_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="12dp" + android:layout_gravity="center_vertical|start" + android:textAlignment="viewStart" + android:singleLine="true" + android:ellipsize="end" + android:visibility="gone" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1" + android:textColor="?android:attr/textColorSecondary"/> + + </LinearLayout> + </LinearLayout> + +</LinearLayout> diff --git a/res/layout/select_source_prompt.xml b/res/layout/select_source_prompt.xml new file mode 100644 index 0000000000..50cf2faa36 --- /dev/null +++ b/res/layout/select_source_prompt.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +android:orientation="vertical" +android:paddingStart="16dip" +android:layout_marginTop="@dimen/bluetooth_dialog_padding_top" +android:layout_width="match_parent" +android:layout_height="match_parent"> + +<EditText + android:id="@+id/broadcastPINcode" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/bluetooth_dialog_padding" + android:layout_marginLeft="@dimen/bluetooth_dialog_padding" + android:layout_marginStart="@dimen/bluetooth_dialog_padding" + android:layout_marginEnd="@dimen/bluetooth_dialog_padding" + android:gravity="center_vertical" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1" + android:ems="16" + android:hint="Broadcast PIN" + android:inputType="text" /> + +</LinearLayout> diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml index 6f83e5cd1e..5472de2333 100644 --- a/res/layout/wifi_dialog.xml +++ b/res/layout/wifi_dialog.xml @@ -399,6 +399,13 @@ android:visibility="gone"/> </LinearLayout> + <CheckBox android:id="@+id/share_this_wifi" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/wifi_item_content" + android:textSize="14sp" + android:text="@string/share_this_wifi" /> + <LinearLayout android:id="@+id/metered_settings_fields" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/res/values-af/arrays.xml b/res/values-af/arrays.xml index 879a0f0e23..cdadf99415 100644 --- a/res/values-af/arrays.xml +++ b/res/values-af/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interne toestelberging"</item> diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 6a85581aae..5c0d35b847 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Geen klank of vibrasie nie"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Geen klank of vibrasie nie en verskyn laer in gespreksafdeling"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kan lui of vibreer op grond van fooninstellings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Wys kennisgewings as \'n banier boaan die skerm wanneer die toestel ontsluit is"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Alle \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"-kennisgewings"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Alle <xliff:g id="APP_NAME">%1$s</xliff:g>-kennisgewings"</string> diff --git a/res/values-am/arrays.xml b/res/values-am/arrays.xml index 794942b2ab..c3320ec5c4 100644 --- a/res/values-am/arrays.xml +++ b/res/values-am/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"የውስጥ መሣሪያ ማከማቻ"</item> diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 0156dfe69a..f383792d79 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ምንም ድምጽ ወይም ንዝረት የለም"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ምንም ድምጽ ወይም ንዝረት የለም እና በውይይት ክፍል ላይ አይታይም"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"በእርስዎ የስልክ ቅንብሮች የሚወሰን ሆኖ ሊደውል ወይም ሊነዝር ይችላል"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ከዚህ በታች ቅድሚያ የሚሰጣቸው ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥ ያለ።"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ከዚህ በታች ቅድሚያ የሚሰጣቸው ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥ ያለ።"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ከዚህ በታች ቅድሚያ የሚሰጣቸው ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥ ያለ።"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"መሣሪያ ሲከፈት፣ ማሳወቂያዎችን እንደ ሰንደቅ በማያ ገጹ አናት ላይ እንዳለ አሳይ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"ሁሉም «<xliff:g id="APP_NAME">%1$s</xliff:g>» ማሳወቂያዎች"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ሁሉም የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያዎች"</string> diff --git a/res/values-ar/arrays.xml b/res/values-ar/arrays.xml index 2f19c460de..debb3c1f89 100644 --- a/res/values-ar/arrays.xml +++ b/res/values-ar/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"مساحة تخزين الجهاز الداخلية"</item> diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 4b271e437e..e7d3c86a38 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -4097,6 +4097,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"بدون صوت أو اهتزاز وتظهر في موضع أسفل في قسم المحادثات"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الهاتف."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"عند فتح قفل الجهاز، تظهر الإشعارات في صورة بانر أعلى الشاشة."</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"يتم عرض هذه الإشعارات أسفل الإشعارات ذات الأولوية. كتم الصوت دائمًا."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"يتم عرض هذه الإشعارات أسفل الإشعارات ذات الأولوية. كتم الصوت دائمًا."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"يتم عرض هذه الإشعارات أسفل الإشعارات ذات الأولوية. كتم الصوت دائمًا."</string> <string name="notification_switch_label" msgid="8029371325967501557">"جميع إشعارات \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"جميع إشعارات \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="default_notification_assistant" msgid="243718059890346442">"الإشعارات التكيّفية"</string> diff --git a/res/values-az/arrays.xml b/res/values-az/arrays.xml index 08a2243083..dc41d16021 100644 --- a/res/values-az/arrays.xml +++ b/res/values-az/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Cihazın daxili yaddaşı"</item> diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index fd75a8769b..09af767b1b 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Səs və ya vibrasiya yoxdur"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Söhbət siyahısının aşağısında səssiz və vibrasiyasız görünür"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Telefon ayarlarına əsasən zəng çala və ya titrəyə bilər"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kilidi açılmış cihazda bütün bildirişlər ekranın yuxarısındakı banner şəklində göstərilsin"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Bütün \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" bildirişləri"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Bütün <xliff:g id="APP_NAME">%1$s</xliff:g> bildirişləri"</string> diff --git a/res/values-b+sr+Latn/arrays.xml b/res/values-b+sr+Latn/arrays.xml index 3c3be0f2e5..598abbb248 100644 --- a/res/values-b+sr+Latn/arrays.xml +++ b/res/values-b+sr+Latn/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Skladište unutrašnjeg uređaja"</item> diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 3d3b671d7b..99e9696e1f 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -3958,6 +3958,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Bez zvuka i vibriranja"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Bez zvuka i vibriranja i prikazuje se u nastavku odeljka za konverzacije"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Može da zvoni ili vibrira u zavisnosti od podešavanja telefona"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kada je uređaj otključan, prikazuje obaveštenja kao baner u vrhu ekrana"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Sva obaveštenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Sva obaveštenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-be/arrays.xml b/res/values-be/arrays.xml index bb99c97cb2..099a9d009c 100644 --- a/res/values-be/arrays.xml +++ b/res/values-be/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Унутраная памяць прылады"</item> diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index fc1d429a7c..ca07432412 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -4006,6 +4006,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без гуку ці вібрацыі"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Паказваецца без гуку ці вібрацыі ў раздзеле размоў"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Калі прылада разблакіравана, паказваць апавяшчэнні ў выглядзе банера ўверсе экрана"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Усе апавяшчэнні праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Усе апавяшчэнні праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> diff --git a/res/values-bg/arrays.xml b/res/values-bg/arrays.xml index bcb64eba42..4591642067 100644 --- a/res/values-bg/arrays.xml +++ b/res/values-bg/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Вътрешно хранилище на устройство"</item> diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index b2625836ce..edbbc8d8fa 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -3914,6 +3914,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без звук или вибриране"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Без звук или вибриране и се показва по-долу в секцията с разговори"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Може да звъни или да вибрира въз основа на настройките за телефона"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Показва се под приоритетните известия. Винаги в тих режим."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Показва се под приоритетните известия. Винаги в тих режим."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Показва се под приоритетните известия. Винаги в тих режим."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Когато устройството е отключено, известията се показват като банер в горната част на екрана"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Всички известия от: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Всички известия от: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -4813,6 +4816,8 @@ <item quantity="other"><xliff:g id="NUMBER">%s</xliff:g> секунди</item> <item quantity="one">1 секунда</item> </plurals> + <string name="qtifeedback_settings_title"> Обратна връзка за хардуер</string> + <string name="qtifeedback_settings_subtitle">Qualcomm Technologies, Inc доклади</string> <string name="automatic_storage_manager_settings" msgid="519158151463974656">"Управление на хранилището"</string> <string name="automatic_storage_manager_text" msgid="6900593059927987273">"За да ви помогне да освободите място за съхранение, мениджърът на хранилището премахва от устройството ви резервните копия на снимки и видеоклипове."</string> <string name="automatic_storage_manager_days_title" msgid="5077286114860539367">"Премахване на снимките и видеоклиповете"</string> diff --git a/res/values-bn/arrays.xml b/res/values-bn/arrays.xml index 03504a4d27..385cf262c4 100644 --- a/res/values-bn/arrays.xml +++ b/res/values-bn/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ইন্টারনাল ডিভাইসের স্টোরেজ"</item> diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index e38a3d6c58..f36ca9170e 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"আওয়াজ করবে না বা ভাইব্রেট হবে না"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"আওয়াজ করবে না বা ভাইব্রেট হবে না এবং কথোপকথন বিভাগের নিচের দিকে দেখা যাবে"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ফোনের সেটিংস অনুযায়ী ফোন রিং বা ভাইব্রেট হতে পারে"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"কোনও ডিভাইস আনলক করা হলে বিজ্ঞপ্তি, স্ক্রিনের উপরে একটি ব্যানার হিসেবে দেখানো হয়"</string> <string name="notification_switch_label" msgid="8029371325967501557">"সমস্ত \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" বিজ্ঞপ্তি"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"সমস্ত <xliff:g id="APP_NAME">%1$s</xliff:g> সংক্রান্ত বিজ্ঞপ্তি"</string> diff --git a/res/values-bs/arrays.xml b/res/values-bs/arrays.xml index 218bed2552..0de1a52c46 100644 --- a/res/values-bs/arrays.xml +++ b/res/values-bs/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Unutrašnja pohrana uređaja"</item> diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 3240bb2827..41d5cf5ddd 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -3960,6 +3960,9 @@ <string name="notification_channel_summary_default" msgid="3674057458265438896">"Može zvoniti ili vibrirati na osnovu postavki vašeg telefona"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kada je uređaj otključan, vidite obavještenja u vidu banera na vrhu ekrana"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Sva obavještenja aplikacije \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Sva obavještenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Prilagodljiva obavještenja"</string> <plurals name="notifications_sent_daily" formatted="false" msgid="1479283620504341566"> diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml index 209a52245a..aa5d2043a1 100644 --- a/res/values-ca/arrays.xml +++ b/res/values-ca/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Emmagatzematge intern del dispositiu"</item> diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 6067808459..bc222a2ecb 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Sense so ni vibració"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Sense so ni vibració i es mostra més avall a la secció de converses"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Pot sonar o vibrar en funció de la configuració del telèfon"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Es mostra a sota de les notificacions prioritàries. Sempre silenciosa."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Es mostra a sota de les notificacions prioritàries. Sempre silenciosa."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Es mostra a sota de les notificacions prioritàries. Sempre silenciosa."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Quan el dispositiu estigui desbloquejat, mostra les notificacions en forma de bàner a la part superior de la pantalla"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Totes les notificacions de: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Totes les notificacions de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-cs/arrays.xml b/res/values-cs/arrays.xml index 7b191af988..b01a7ec8f1 100644 --- a/res/values-cs/arrays.xml +++ b/res/values-cs/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interní úložiště zařízení"</item> diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 11b93e42df..bfadd874d7 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -4004,6 +4004,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Žádný zvuk ani vibrace"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Žádný zvuk ani vibrace a zobrazuje se níže v sekci konverzací"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Vyzvání nebo vibruje podle nastavení telefonu"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Zobrazí oznámení s nižší prioritou. Vždy tichý režim."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Zobrazí oznámení s nižší prioritou. Vždy tichý režim."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Zobrazí oznámení s nižší prioritou. Vždy tichý režim."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Když je zařízení odemčené, zobrazovat oznámení jako banner v horní části obrazovky"</string> <string name="notification_switch_label" msgid="8029371325967501557">"<xliff:g id="APP_NAME">%1$s</xliff:g>: všechna oznámení"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Všechna oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-da/arrays.xml b/res/values-da/arrays.xml index 103f57d79d..2562fcf944 100644 --- a/res/values-da/arrays.xml +++ b/res/values-da/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Intern lagerplads på enheden"</item> diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 9466c0657d..3dbf489ccd 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ingen lyd eller vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ingen lyd eller vibration, og den vises længere nede i samtalesektionen"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kan ringe eller vibrere baseret på telefonens indstillinger"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Viser notifikationer med lav prioritet. Altid lydløs."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Viser notifikationer med lav prioritet. Altid lydløs."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Viser notifikationer med lav prioritet. Altid lydløs."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Når enheden er låst op, vises notifikationer som et banner øverst på skærmen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Alle notifikationer fra \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Alle notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml index 2e869b7e4b..6ea46aff16 100644 --- a/res/values-de/arrays.xml +++ b/res/values-de/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interner Gerätespeicher"</item> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 8a4be36a99..12aa65639b 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Kein Ton und keine Vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Kein Ton und keine Vibration, erscheint weiter unten im Bereich „Unterhaltungen“"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kann klingeln oder vibrieren, abhängig von den Telefoneinstellungen"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Bei entsperrtem Gerät Benachrichtigungen als Banner oben auf dem Bildschirm anzeigen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Alle Benachrichtigungen von „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Alle Benachrichtigungen von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-el/arrays.xml b/res/values-el/arrays.xml index 725d8c2c0c..82d04f015f 100644 --- a/res/values-el/arrays.xml +++ b/res/values-el/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Εσωτερικός αποθηκευτικός χώρος της συσκευής"</item> diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 7ac8306356..61d89272b0 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Χωρίς ήχο ή δόνηση"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Χωρίς ήχο ή δόνηση και εμφανίζεται χαμηλά στην ενότητα συζητήσεων"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Όταν η συσκευή είναι ξεκλειδωμένη, οι ειδοποιήσεις εμφανίζονται ως banner επάνω στην οθόνη"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Όλες οι ειδοποιήσεις \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Όλες οι ειδοποιήσεις <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-en-rAU/arrays.xml b/res/values-en-rAU/arrays.xml index d4852dd899..c2626fbdd3 100644 --- a/res/values-en-rAU/arrays.xml +++ b/res/values-en-rAU/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Internal device storage"</item> diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 912a557ebf..dce20bae07 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"No sound or vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No sound or vibration and appears lower in conversation section"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"May ring or vibrate based on phone settings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Displays below priority notifications. Always silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"When device is unlocked, show notifications as a banner across the top of the screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"All \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' notifications"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index 15b1f31853..894a78e805 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"No sound or vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No sound or vibration and appears lower in conversation section"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"May ring or vibrate based on phone settings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Displays below priority notifications. Always silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"When device is unlocked, show notifications as a banner across the top of the screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"All \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' notifications"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string> diff --git a/res/values-en-rGB/arrays.xml b/res/values-en-rGB/arrays.xml index d4852dd899..c2626fbdd3 100644 --- a/res/values-en-rGB/arrays.xml +++ b/res/values-en-rGB/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Internal device storage"</item> diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 19840a7a25..e047c9f943 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"No sound or vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No sound or vibration and appears lower in conversation section"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"May ring or vibrate based on phone settings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Displays below priority notifications. Always silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"When device is unlocked, show notifications as a banner across the top of the screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"All \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' notifications"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string> diff --git a/res/values-en-rIN/arrays.xml b/res/values-en-rIN/arrays.xml index d4852dd899..c2626fbdd3 100644 --- a/res/values-en-rIN/arrays.xml +++ b/res/values-en-rIN/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Internal device storage"</item> diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 0adec02837..709aa82b55 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"No sound or vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No sound or vibration and appears lower in conversation section"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"May ring or vibrate based on phone settings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Displays below priority notifications. Always silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"When device is unlocked, show notifications as a banner across the top of the screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"All \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' notifications"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml index c812f7f1ca..de5abfae83 100644 --- a/res/values-en-rXC/strings.xml +++ b/res/values-en-rXC/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"No sound or vibration"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No sound or vibration and appears lower in conversation section"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"May ring or vibrate based on phone settings"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Displays below priority notifications. Always silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Displays below priority notifications. Always silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"When device is unlocked, show notifications as a banner across the top of the screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"All \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" notifications"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string> diff --git a/res/values-es-rUS/arrays.xml b/res/values-es-rUS/arrays.xml index 74bb789b54..ffb9f67066 100644 --- a/res/values-es-rUS/arrays.xml +++ b/res/values-es-rUS/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Dispositivo de almacenamiento interno"</item> diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 92fbdd5fa6..f8f2a213c5 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Puede sonar o vibrar en función de la configuración del teléfono."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Con el dispositivo desbloqueado, mostrar notificaciones como banner en la parte superior de la pantalla"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Todas las notificaciones de \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Todas las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notificaciones adaptables"</string> diff --git a/res/values-es/arrays.xml b/res/values-es/arrays.xml index 9996c21642..08f1e03a20 100644 --- a/res/values-es/arrays.xml +++ b/res/values-es/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Almacenamiento de dispositivo interno"</item> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index b3d9c2b27a..c8e020582a 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Puede sonar o vibrar según los ajustes del teléfono"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Cuando el dispositivo esté desbloqueado, muestra las notificaciones en la parte superior"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Todas las notificaciones de \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Todas las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notificaciones adaptativas"</string> @@ -4812,6 +4815,8 @@ <item quantity="one">1 segundo</item> </plurals> <string name="automatic_storage_manager_settings" msgid="519158151463974656">"Gestionar almacenamiento"</string> + <string name="qtifeedback_settings_title">Comentarios sobre el hardware</string> + <string name="qtifeedback_settings_subtitle">Informes de Qualcomm Technologies, Inc</string> <string name="automatic_storage_manager_text" msgid="6900593059927987273">"Para liberar espacio, el Administrador de Almacenamiento borrará de tu dispositivo las fotos y vídeos que tengan copia de seguridad."</string> <string name="automatic_storage_manager_days_title" msgid="5077286114860539367">"Borrar fotos y vídeos"</string> <string name="automatic_storage_manager_preference_title" msgid="3483357910142595444">"Administrador de almacenamiento"</string> diff --git a/res/values-et/arrays.xml b/res/values-et/arrays.xml index bcdb33d2fd..d331a232ae 100644 --- a/res/values-et/arrays.xml +++ b/res/values-et/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Seadme sisemine salvestusruum"</item> diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 2265879140..0867d60169 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -3914,6 +3914,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ilma heli ja vibreerimiseta"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ilma heli ja vibreerimiseta, kuvatakse vestluste jaotises allpool"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Võib telefoni seadete põhjal heliseda või vibreerida"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kui seade on avatud, kuvatakse märguanded bännerina ekraanikuva ülaosas"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Kõik tüüpi „<xliff:g id="APP_NAME">%1$s</xliff:g>” märguanded"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Kõik rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguanded"</string> diff --git a/res/values-eu/arrays.xml b/res/values-eu/arrays.xml index a95a0a1d47..7bc6dd4c0b 100644 --- a/res/values-eu/arrays.xml +++ b/res/values-eu/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Gailuaren barneko memoria"</item> diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 8a97c4b09d..e15783c482 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ez du tonurik jotzen edo dar-dar egiten"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ez du tonurik jotzen edo dar-dar egiten, eta elkarrizketaren atalaren behealdean agertzen da"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Tonua jo edo dar-dar egin dezake, telefonoaren ezarpenen arabera"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Gailua desblokeatuta dagoenean, erakutsi jakinarazpenak banda gisa pantailaren goialdean"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" aplikazioaren jakinarazpen guztiak"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpen guztiak"</string> diff --git a/res/values-fa/arrays.xml b/res/values-fa/arrays.xml index b8b0b0d2c6..58c4f79293 100644 --- a/res/values-fa/arrays.xml +++ b/res/values-fa/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"دستگاه ذخیرهسازی داخلی"</item> diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 5d5e979561..71bbe3d780 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"بدون صدا یا لرزش"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"بدون صدا و لرزش در پایین بخش مکالمه نشان داده میشود"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"بسته به تنظیمات ممکن است تلفن زنگ بزند یا لرزش داشته باشد"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"وقتی قفل دستگاه باز میشود، اعلانها بهصورت برنمایی در بالای صفحهنمایش نشان داده شود"</string> <string name="notification_switch_label" msgid="8029371325967501557">"همه اعلانهای «<xliff:g id="APP_NAME">%1$s</xliff:g>»"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"همه اعلانهای <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-fi/arrays.xml b/res/values-fi/arrays.xml index 4c39095276..e7703d3dc9 100644 --- a/res/values-fi/arrays.xml +++ b/res/values-fi/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Laitteen sisäinen tallennustila"</item> diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 6fc37f54ab..627aeec08f 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ei ääntä tai värinää ja näkyy alempana keskusteluosiossa"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Voi soida tai väristä puhelimen asetuksista riippuen"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kun laitteen lukitus on avattuna, näytä ilmoitukset bannerina sivun yläreunassa"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Kaikki ilmoitukset: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Kaikki ilmoitukset: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Mukautuvat ilmoitukset"</string> diff --git a/res/values-fr-rCA/arrays.xml b/res/values-fr-rCA/arrays.xml index ab61755974..f846cd56f6 100644 --- a/res/values-fr-rCA/arrays.xml +++ b/res/values-fr-rCA/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Mémoire interne du mobile"</item> diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 86a2cec2e8..7e37cbce9e 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Aucun son ni vibration, et s\'affiche plus bas dans la section des conversations"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Peut sonner ou vibrer, selon les paramètres du téléphone"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Lorsque l\'appareil est déverrouillé, afficher les notifications dans une bannière dans le haut de l\'écran"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Toutes les notifications de « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Toutes les notifications de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notifications adaptatives"</string> diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml index 32ce72e7d8..b8d6364503 100644 --- a/res/values-fr/arrays.xml +++ b/res/values-fr/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Mémoire interne du mobile"</item> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 70456155e0..d3b9495098 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ni son, ni vibreur"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ni son, ni vibreur ; affichage plus bas dans la section \"Conversations\""</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Son ou vibreur, selon les paramètres du téléphone"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Lorsque l\'appareil est déverrouillé, afficher les notifications dans une bannière en haut de l\'écran"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Toutes les notifications de \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Toutes les notifications de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-fr/strings_group.xml b/res/values-fr/strings_group.xml new file mode 100644 index 0000000000..6e4cdf3f3b --- /dev/null +++ b/res/values-fr/strings_group.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">"identifiant de groupe"</string> + <string name="refresh_group">"Rafraîchir le groupe"</string> + <string name="cancel_refresh_group">"Annuler l'actualisation"</string> + <string name="disconnect_group">"Déconnecter le groupe"</string> + <string name="connect_group">"Connecter le groupe"</string> + <string name="forget_group">"Groupe Oublier"</string> + <string name="group_previously_connected_screen_title">"Groupe précédemment connecté"</string> + <string name="connected_group">"Groupe connecté"</string> + <string name="group_connected_device_media_device_title">"Appareils multimédia de groupe"</string> + <string name="group_settings">"Groupe"</string> + <string name="previous_connected_see_all_groups">"Groupe précédemment connecté"</string> + <string name="group_options">"Options de groupe"</string> + <string name="group_connected_devices">"Des appareils connectés"</string> + <string name="group_active_devices">"Appareils actifs"</string> + <string name="group_bonded_devices">"Dispositif lié"</string> + <string name="active">"Active"</string> + <string name="groupaudio_unpair_dialog_title">"Oubliez le groupe?"</string> + <string name="groupaudio_unpair_dialog_body">"Votre téléphone ne sera plus associé à l'ensemble"<xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">"Groupe Oublier"</string> + <string name="group_apply_changes_dialog_title">Appliquer les modifications à tous les membres du groupe ?</string> + <string name="group_confirm_dialog_body">Mêmes changements applicables à tous les membres du groupe <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">Appliquer</string> +</resources> diff --git a/res/values-gl/arrays.xml b/res/values-gl/arrays.xml index 2ae1b1ef07..83f1bc6dd8 100644 --- a/res/values-gl/arrays.xml +++ b/res/values-gl/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Almacenamento interno do dispositivo"</item> diff --git a/res/values-gu/arrays.xml b/res/values-gu/arrays.xml index 75e2e7f69b..d1994a34d3 100644 --- a/res/values-gu/arrays.xml +++ b/res/values-gu/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"આંતરિક ડિવાઇસ સ્ટોરેજ"</item> diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index b8b5633bb5..a8087ba1fc 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી અને વાતચીત વિભાગમાં તે વધુ નીચેની દિશાએ દેખાય છે"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ફોન સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ડિવાઇસ અનલૉક થયેલું હોય ત્યારે, સ્ક્રીનના ઉપરના ભાગ પર બૅનરના સ્વરૂપમાં નોટિફિકેશન બતાવો"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"ના તમામ નોટિફિકેશન"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g>ના બધા નોટિફિકેશન"</string> diff --git a/res/values-hi/arrays.xml b/res/values-hi/arrays.xml index e4e1ad52e0..f1dad8974b 100644 --- a/res/values-hi/arrays.xml +++ b/res/values-hi/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"आंतरिक डिवाइस मेमोरी"</item> diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index d017a6e0b5..fddbfee31e 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और \'बातचीत\', सेक्शन में सबसे नीचे दिखती है"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"जब डिवाइस अनलॉक हो, तो स्क्रीन के सबसे ऊपर बैनर के रूप में सूचनाएं दिखाएं"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" की सभी सूचनाएं"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> की सभी सूचनाएं"</string> diff --git a/res/values-hr/arrays.xml b/res/values-hr/arrays.xml index 79325775d2..467c180e19 100644 --- a/res/values-hr/arrays.xml +++ b/res/values-hr/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interno pohranjivanje na uređaj"</item> diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 8f4fdad10f..1e3966af61 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -3960,6 +3960,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Bez zvuka ili vibracije"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Bez zvuka ili vibracije i prikazuje se pri dnu odjeljka razgovora"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Možda će zvoniti ili vibrirati, ovisno o postavkama telefona"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Prikazuje obavijesti nižeg prioriteta. Uvijek bešumno."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Prikazuje obavijesti nižeg prioriteta. Uvijek bešumno."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Prikazuje obavijesti nižeg prioriteta. Uvijek bešumno."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kad je uređaj otključan, prikaži obavijesti kao natpis pri vrhu zaslona"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Sve obavijesti aplikacije \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Sve obavijesti aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-hu/arrays.xml b/res/values-hu/arrays.xml index 13ab2f40fb..2b21e7bb54 100644 --- a/res/values-hu/arrays.xml +++ b/res/values-hu/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Belső tárhely"</item> diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 2894335437..856c595eea 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Nincs hang és rezgés"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Nincs hang és rezgés, továbbá lejjebb jelenik meg a beszélgetések szakaszában"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"A telefonbeállítások alapján csöröghet és rezeghet"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Feloldott állapotban az értesítések megjelenítése szalag formájában a képernyő felső részén"</string> <string name="notification_switch_label" msgid="8029371325967501557">"A(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” összes értesítése"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Az összes <xliff:g id="APP_NAME">%1$s</xliff:g>-értesítés"</string> diff --git a/res/values-hy/arrays.xml b/res/values-hy/arrays.xml index d84bb54e63..981a899174 100644 --- a/res/values-hy/arrays.xml +++ b/res/values-hy/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Սարքի ներքին պահոց"</item> diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 9371bba522..bb1ef4fd6f 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Հայտնվում է զրույցների ցանկի ներքևում, առանց ձայնի և թրթռոցի"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Կարող է զնգալ կամ թրթռալ (հեռախոսի կարգավորումներից կախված)"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Երբ սարքն ապակողպված է, ծանուցումները ցույց տալ էկրանի վերևի ազդերիզի տեսքով"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string> <string name="notification_switch_label" msgid="8029371325967501557">"«<xliff:g id="APP_NAME">%1$s</xliff:g>» պիտակով բոլոր ծանուցումները"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի բոլոր ծանուցումները"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Հարմարվող ծանուցումներ"</string> diff --git a/res/values-in/arrays.xml b/res/values-in/arrays.xml index f1c9c1067b..ca39167713 100644 --- a/res/values-in/arrays.xml +++ b/res/values-in/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Penyimpanan perangkat internal"</item> diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index c0c2566022..568dcd3f0b 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Tidak ada suara atau getaran dan ditampilkan lebih rendah di bagian percakapan"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Dapat berdering atau bergetar berdasarkan setelan ponsel"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Saat perangkat tidak terkunci, notifikasi ditampilkan sebagai banner di bagian atas layar"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Semua notifikasi \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Semua notifikasi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notifikasi Adaptif"</string> diff --git a/res/values-is/arrays.xml b/res/values-is/arrays.xml index 89e2f1827b..b24f342e1d 100644 --- a/res/values-is/arrays.xml +++ b/res/values-is/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Innbyggð geymsla tækis"</item> diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index d3e5eef711..d664e314e4 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ekkert hljóð eða titringur"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ekkert hljóð eða titringur og birtist neðar í samtalshluta"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Gæti hringt eða titrað eftir stillingum símans"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Birta tilkynningar á borða efst á skjánum þegar tækið er opið"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Allar tilkynningar frá „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Allar tilkynningar frá <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-it/arrays.xml b/res/values-it/arrays.xml index 287eec6a58..5d944a361b 100644 --- a/res/values-it/arrays.xml +++ b/res/values-it/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Memorizzazione su dispositivo interno"</item> diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index e408720954..0c4d900689 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Nessun suono o vibrazione"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Nessun suono o vibrazione e appare più in basso nella sezione delle conversazioni"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Può suonare o vibrare in base alle impostazioni del telefono"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Quando il dispositivo è sbloccato, mostra le notifiche come banner in cima allo schermo"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Tutte le notifiche di \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Tutte le notifiche di <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-iw/arrays.xml b/res/values-iw/arrays.xml index 89efdcc6a7..4c9ee84961 100644 --- a/res/values-iw/arrays.xml +++ b/res/values-iw/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"אחסון פנימי במכשיר"</item> diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 8e62ae00dc..6f77433098 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -4006,6 +4006,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ללא צליל או רטט"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ללא צליל או רטט ומופיעה למטה בקטע התראות השיחה"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות הטלפון"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"מציגה התראות בעדיפות נמוכה. תמיד שקטה."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"מציגה התראות בעדיפות נמוכה. תמיד שקטה."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"מציגה התראות בעדיפות נמוכה. תמיד שקטה."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"כשהמכשיר לא נעול, התראות יוצגו כבאנר בראש המסך"</string> <string name="notification_switch_label" msgid="8029371325967501557">"כל ההתראות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"כל ההתראות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-ja/arrays.xml b/res/values-ja/arrays.xml index 5120065ce0..14a5c52409 100644 --- a/res/values-ja/arrays.xml +++ b/res/values-ja/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"内部デバイスストレージ"</item> diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 9c67fd1402..caf7ad2482 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"着信音もバイブレーションも無効になります"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"着信音もバイブレーションも無効になり会話セクションの下に表示されます"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"デバイスのロックが解除されているとき、画面上部にバナーとして通知を表示します"</string> <string name="notification_switch_label" msgid="8029371325967501557">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」のすべての通知"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> のすべての通知"</string> diff --git a/res/values-ja/strings_group.xml b/res/values-ja/strings_group.xml new file mode 100644 index 0000000000..e6a9b9d37b --- /dev/null +++ b/res/values-ja/strings_group.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">グループID</string> + <string name="refresh_group">グループの更新</string> + <string name="cancel_refresh_group">更新をキャンセル</string> + <string name="disconnect_group">グループを切断します</string> + <string name="connect_group">グループを接続する</string> + <string name="forget_group">グループを忘れる</string> + <string name="group_previously_connected_screen_title">以前に接続したグループ</string> + <string name="group_settings">グループ</string> + <string name="previously_connected_group_screen_title">以前に接続したグループ</string> + <string name="previous_connected_see_all_groups">すべてのグループを見る</string> + <string name="group_options">グループオプション</string> + <string name="group_connected_devices">接続されたデバイス</string> + <string name="group_active_devices">アクティブデバイス </string> + <string name="group_bonded_devices">結合デバイス</string> + <string name="active">アクティブ</string> + <string name="groupaudio_unpair_dialog_title">グループを忘れますか?</string> + <string name="groupaudio_unpair_dialog_body">お使いの携帯電話は セットとペアリングされなくなりました <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">グループを忘れる</string> + <string name="group_apply_changes_dialog_title">すべてのグループメンバーに変更を適用する ? </string> + <string name="group_confirm_dialog_body">グループのすべてのメンバーに同じ変更が適用されます <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">適用する</string> +</resources> diff --git a/res/values-ka/arrays.xml b/res/values-ka/arrays.xml index 2f983afff3..86f7bb0f19 100644 --- a/res/values-ka/arrays.xml +++ b/res/values-ka/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"შიდა მოწყობილობის მეხსიერება"</item> diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index d11c6d0bd6..da1b8b2742 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ხმისა და ვიბრაციის გარეშე"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ხმისა და ვიბრაციის გარეშე, ჩნდება მიმოწერების სექციის ქვედა ნაწილში"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმია."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმია."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმია."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"როცა მოწყობილობა განბლოკილია, შეტყობინებები გამოჩნდეს ეკრანის ზედა ნაწილზე გადაჭიმული ბანერის სახით"</string> <string name="notification_switch_label" msgid="8029371325967501557">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“-ის ყველა შეტყობინება"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის ყველა შეტყობინება"</string> diff --git a/res/values-kk/arrays.xml b/res/values-kk/arrays.xml index 8097032a51..8d81018296 100644 --- a/res/values-kk/arrays.xml +++ b/res/values-kk/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Ішкі құрылғы жады"</item> diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 932a83b286..fc37706ab4 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -3910,8 +3910,11 @@ <string name="convo_not_supported_summary" msgid="4285471045268268048">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы әңгіме функцияларының көбісін қолдамайды. Әңгімені маңызды деп орната алмайсыз және олар қалқыма хабарлар түрінде шықпайды."</string> <string name="notification_channel_summary_min" msgid="8823399508450176842">"Ашылмалы мәзірде хабарландыруларды бір жолға жию"</string> <string name="notification_channel_summary_low" msgid="5549662596677692000">"Дыбыс не діріл болмайды."</string> - <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Дыбыс не діріл болмайды, әңгімелер бөлімінің төмен жағында тұрады."</string> - <string name="notification_channel_summary_default" msgid="3674057458265438896">"Телефон параметрлеріне байланысты дыбыстық сигнал не діріл болуы мүмкін."</string> + <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Дыбыс не діріл болмайды, әңгімелер бөлімінің төмен жағында тұрады."</string> + <string name="notification_channel_summary_default" msgid="3674057458265438896">"Телефон параметрлеріне байланысты дыбыстық сигнал не діріл болуы мүмкін."</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Құрылғының құлпы ашылғанда, хабарландырулар экранның жоғарғы жағында баннер ретінде көрсетіледі."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Барлық \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" хабарландырулары"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Барлық <xliff:g id="APP_NAME">%1$s</xliff:g> хабарландырулары"</string> diff --git a/res/values-km/arrays.xml b/res/values-km/arrays.xml index 0dbb161c61..2671845bff 100644 --- a/res/values-km/arrays.xml +++ b/res/values-km/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ឧបករណ៍ផ្ទុកខាងក្នុង"</item> diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 58de8ab99f..8e49f456fa 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_channel_summary_high" msgid="3411637309360617621">"បង្ហាញការជូនដំណឹងជាផ្ទាំងបដានៅផ្នែកខាងលើអេក្រង់ នៅពេលឧបករណ៍មិនជាប់សោ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"ការជូនដំណឹងទាំងអស់ពី \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ការជូនដំណឹងទាំងអស់ពី <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"បង្ហាញខាងក្រោមការជូនដំណឹងអាទិភាព។ បិទសំឡេងជានិច្ច។"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"បង្ហាញខាងក្រោមការជូនដំណឹងអាទិភាព។ បិទសំឡេងជានិច្ច។"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"បង្ហាញខាងក្រោមការជូនដំណឹងអាទិភាព។ បិទសំឡេងជានិច្ច។"</string> <string name="default_notification_assistant" msgid="243718059890346442">"ការជូនដំណឹងដែលមានភាពបត់បែន"</string> <plurals name="notifications_sent_daily" formatted="false" msgid="1479283620504341566"> <item quantity="other">ការជូនដំណឹង ~<xliff:g id="NUMBER_1">%d</xliff:g> ក្នុងមួយថ្ងៃ</item> diff --git a/res/values-kn/arrays.xml b/res/values-kn/arrays.xml index 31c4b4d25a..3c69c609fa 100644 --- a/res/values-kn/arrays.xml +++ b/res/values-kn/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ಆಂತರಿಕ ಸಾಧನ ಸಂಗ್ರಹಣೆ"</item> diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 6ce6d1ad1a..875d173a0e 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್ ಆಗುವುದಿಲ್ಲ, ಸಂಭಾಷಣೆ ವಿಭಾಗದ ಕೆಳಭಾಗದಲ್ಲಿ ಗೋಚರಿಸುತ್ತದೆ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಿದಾಗ, ಅಧಿಸೂಚನೆಗಳನ್ನು ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ ಬ್ಯಾನರ್ ಆಗಿ ತೋರಿಸಿ"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ಆದ್ಯತೆಯ ಅಧಿಸೂಚನೆಗಳ ಅಡಿಯಲ್ಲಿ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಶಬ್ದ."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ಆದ್ಯತೆಯ ಅಧಿಸೂಚನೆಗಳ ಅಡಿಯಲ್ಲಿ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಶಬ್ದ."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ಆದ್ಯತೆಯ ಅಧಿಸೂಚನೆಗಳ ಅಡಿಯಲ್ಲಿ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಶಬ್ದ."</string> <string name="notification_switch_label" msgid="8029371325967501557">"ಎಲ್ಲಾ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ಅಧಿಸೂಚನೆಗಳು"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ಎಲ್ಲಾ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಧಿಸೂಚನೆಗಳು"</string> <string name="default_notification_assistant" msgid="243718059890346442">"ಅಡಾಪ್ಟಿವ್ ಅಧಿಸೂಚನೆಗಳು"</string> diff --git a/res/values-ko/arrays.xml b/res/values-ko/arrays.xml index af1b069e6f..d823054d91 100644 --- a/res/values-ko/arrays.xml +++ b/res/values-ko/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"내부 기기 저장용량"</item> diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index cf9e5126e5..dce1b2c198 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"소리나 진동이 울리지 않으며 대화 섹션 하단에 표시됨"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있음"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"기기가 잠금 해제되어 있을 때 화면 상단에 알림 배너 표시"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string> <string name="notification_switch_label" msgid="8029371325967501557">"모든 ‘<xliff:g id="APP_NAME">%1$s</xliff:g>’ 알림"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"모든 <xliff:g id="APP_NAME">%1$s</xliff:g> 알림"</string> <string name="default_notification_assistant" msgid="243718059890346442">"적응형 알림"</string> diff --git a/res/values-ko/strings_group.xml b/res/values-ko/strings_group.xml new file mode 100644 index 0000000000..ceb4022f30 --- /dev/null +++ b/res/values-ko/strings_group.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">그룹 ID</string> + <string name="refresh_group">그룹 새로 고침</string> + <string name="cancel_refresh_group">새로 고침 취소</string> + <string name="disconnect_group">그룹 연결 해제</string> + <string name="connect_group">연결 그룹</string> + <string name="forget_group">그룹 잊어 버려</string> + <string name="group_previously_connected_screen_title">이전에 연결된 그룹</string> + <string name="group_settings">그룹</string> + <string name="previously_connected_group_screen_title">이전에 연결된 그룹</string> + <string name="previous_connected_see_all_groups">모든 그룹보기</string> + <string name="group_options">그룹 옵션</string> + <string name="group_connected_devices">연결된 장치</string> + <string name="group_active_devices">활성 장치</string> + <string name="group_bonded_devices">보세 장치</string> + <string name="active">유효한</string> + <string name="groupaudio_unpair_dialog_title">그룹을 잊으셨습니까?</string> + <string name="groupaudio_unpair_dialog_body">휴대 전화가 더 이상 세트와 페어링되지 않습니다. + <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">그룹 잊어 버려</string> + <string name="group_apply_changes_dialog_title">모든 그룹 구성원에게 변경 사항 적용 ? </string> + <string name="group_confirm_dialog_body">그룹의 모든 구성원에게 적용되는 동일한 변경 사항 <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">대다</string> +</resources> diff --git a/res/values-ky/arrays.xml b/res/values-ky/arrays.xml index cb59e7ae4a..1143a041da 100644 --- a/res/values-ky/arrays.xml +++ b/res/values-ky/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Ички түзмөк эстутуму"</item> diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index c0c8c1ae07..a93df77941 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Үнү чыкпайт же дирилдебейт жана сүйлөшүүлөр тизмесинин ылдый жагында көрүнөт"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Телефон кулпуланып турганда, билдирмелер экрандын жогору жагында баннер түрүндө көрүнөт"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" колдонмосундагы бардык билдирмелер"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунун бардык билдирмелери"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Ыңгайлаштырылуучу билдирмелер"</string> diff --git a/res/values-lo/arrays.xml b/res/values-lo/arrays.xml index b03d165571..a68c08e3fa 100644 --- a/res/values-lo/arrays.xml +++ b/res/values-lo/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນພາຍໃນອຸປະກອນ"</item> diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 49e05bb3d0..505eb5621c 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ ແລະ ປາກົດຢູ່ທາງລຸ່ມຂອງພາກສ່ວນການສົນທະນາ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າໂທລະສັບ"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ເມື່ອປົດລັອກອຸປະກອນແລ້ວ, ໃຫ້ສະແດງການແຈ້ງເຕືອນເປັນປ້າຍຢູ່ເທິງສຸດຂອງໜ້າຈໍ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"ການແຈ້ງເຕືອນ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ທັງໝົດ"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ການແຈ້ງເຕືອນ <xliff:g id="APP_NAME">%1$s</xliff:g> ທັງໝົດ"</string> diff --git a/res/values-lt/arrays.xml b/res/values-lt/arrays.xml index b9a00e74a2..f16681c9fb 100644 --- a/res/values-lt/arrays.xml +++ b/res/values-lt/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Vidinė įrenginio saugykla"</item> diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index c5fdb2be61..cbc1d6e722 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -4004,6 +4004,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Neskamba ir nevibruoja"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Neskamba, nevibruoja ir rodoma apatinėje pokalbių skilties dalyje"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Gali skambėti arba vibruoti, atsižvelgiant į telefono nustatymus"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kai įrenginys atrakintas, pranešimai rodomi kaip reklamjuostė ekrano viršuje"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Visi „<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimai"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Visi „<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimai"</string> diff --git a/res/values-lv/arrays.xml b/res/values-lv/arrays.xml index 8e2c787b43..00bd82a89f 100644 --- a/res/values-lv/arrays.xml +++ b/res/values-lv/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Iekšējā ierīces krātuve"</item> diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index f7482399ca..bc0fce7355 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -3958,6 +3958,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Nav skaņas signāla vai vibrācijas"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Nav skaņas signāla vai vibrācijas, kā arī atrodas tālāk sarunu sadaļā"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Rādīt paziņojumus reklāmkarogā ekrāna augšdaļā, ja ierīce ir atbloķēta"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Visi lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumi"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Visi lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumi"</string> diff --git a/res/values-mk/arrays.xml b/res/values-mk/arrays.xml index f116f62153..062ea864af 100644 --- a/res/values-mk/arrays.xml +++ b/res/values-mk/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Внатрешен капацитет"</item> diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 1d360964ab..00d89a75de 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -3914,7 +3914,10 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без звук или вибрации"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Без звук или вибрации и се појавува под делот со разговори"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string> - <string name="notification_channel_summary_high" msgid="3411637309360617621">"Кога уредот е отклучен, прикажувај ги известувањата како банер на горниот дел од екранот"</string> + <string name="notification_channel_summary_high" msgid="3411637309360617621">"Кога уредот е отклучен, прикажувај ги известувањата како банер на горниот дел од екранот"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Сите известувања од „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Сите известувања од <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Адаптивни известувања"</string> diff --git a/res/values-ml/arrays.xml b/res/values-ml/arrays.xml index dafc6e2d81..f087d2fc81 100644 --- a/res/values-ml/arrays.xml +++ b/res/values-ml/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ആന്തരിക ഉപകരണ സ്റ്റോറേജ്"</item> diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 24e6c53ce9..b2e1c1f0a7 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല, സംഭാഷണ വിഭാഗത്തിന് താഴെയായി ദൃശ്യമാകും"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ്/വൈബ്രേറ്റ് ചെയ്യും"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ഉപകരണം അൺലോക്ക് ചെയ്തിരിക്കുമ്പോൾ അറിയിപ്പുകളെ സ്ക്രീനിന്റെ മുകളിൽ ഒരു ബാനറായി കാണിക്കുക"</string> <string name="notification_switch_label" msgid="8029371325967501557">"എല്ലാ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" അറിയിപ്പുകളും"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"എല്ലാ <xliff:g id="APP_NAME">%1$s</xliff:g> അറിയിപ്പുകളും"</string> diff --git a/res/values-mn/arrays.xml b/res/values-mn/arrays.xml index 832563be2f..c410d33213 100644 --- a/res/values-mn/arrays.xml +++ b/res/values-mn/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Дотоод төхөөрөмжийн сан"</item> diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 4c122807b7..bc57db43c9 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Дуу эсвэл чичиргээ байхгүй"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Дуу эсвэл чичиргээ байхгүй бөгөөд харилцан ярианы хэсгийн доод талд харагдана"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж дуугүй байна."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж дуугүй байна."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж дуугүй байна."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Төхөөрөмжийн түгжээг тайлсан үед мэдэгдлүүдийг дэлгэцийн дээд хэсэгт баннер болгож харуулах"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"-н бүх мэдэгдэл"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н бүх мэдэгдэл"</string> diff --git a/res/values-mr/arrays.xml b/res/values-mr/arrays.xml index f0e2d930d1..647287fb00 100644 --- a/res/values-mr/arrays.xml +++ b/res/values-mr/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"अंतर्गत डिव्हाइस स्टोरेज"</item> diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index bcb32c4742..8aad353963 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"आवाज किंवा व्हायब्रेशन नाही"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"आवाज किंवा व्हायब्रेशन नाही आणि संभाषण विभागात सर्वात तळाशी दिसते"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"फोन सेटिंग्जच्या आधारावर रिंग किंवा व्हायब्रेट होऊ शकतो"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"डिव्हाइस अनलॉक असताना, स्क्रीनच्या सर्वात वरती बॅनर म्हणून सूचना दाखवा"</string> <string name="notification_switch_label" msgid="8029371325967501557">"सर्व \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" सूचना"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string> diff --git a/res/values-ms/arrays.xml b/res/values-ms/arrays.xml index 274e55ccbf..c79ba3ee19 100644 --- a/res/values-ms/arrays.xml +++ b/res/values-ms/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Storan dalaman peranti"</item> diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 1ecc106133..d7215b6700 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Tiada bunyi atau getaran dan muncul di sebelah bawah dalam bahagian perbualan"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Mungkin berbunyi atau bergetar berdasarkan tetapan telefon"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Apabila kunci peranti dibuka, tunjukkan pemberitahuan sebagai sepanduk di bahagian atas skrin"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Semua pemberitahuan \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Semua pemberitahuan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Pemberitahuan Boleh Suai"</string> diff --git a/res/values-my/arrays.xml b/res/values-my/arrays.xml index 5f8baa19d2..b4682372eb 100644 --- a/res/values-my/arrays.xml +++ b/res/values-my/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"စက်တွင်းသိုလှောင်ကိရိယာ"</item> diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index f5b4ded6b7..69f73b9142 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ၊ စကားဝိုင်းကဏ္ဍ၏ အောက်ပိုင်းတွင် မြင်ရသည်"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ဖုန်းဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် သို့မဟုတ် တုန်ခါနိုင်သည်"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲအသံတိတ်ရန်။"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲအသံတိတ်ရန်။"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲအသံတိတ်ရန်။"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"စက်ကို လော့ခ်ဖွင့်ထားပါက အကြောင်းကြားချက်များကို မျက်နှာပြင်၏ထိပ်တွင် နဖူးစည်းအဖြစ် ပြသပါ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" အကြောင်းကြားချက်အားလုံး"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> အကြောင်းကြားချက် အားလုံး"</string> diff --git a/res/values-nb/arrays.xml b/res/values-nb/arrays.xml index c212dbbb34..3ad1faac0b 100644 --- a/res/values-nb/arrays.xml +++ b/res/values-nb/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Intern lagringsenhet"</item> diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 870e5189ab..30dd0c8ee2 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Ingen lyd eller vibrering"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Ingen lyd eller vibrering, og vises lavere i samtaledelen"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kan ringe eller vibrere basert på telefoninnstillingene"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Vises under prioritetsvarsler. Alltid lydløs."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Vises under prioritetsvarsler. Alltid lydløs."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Vises under prioritetsvarsler. Alltid lydløs."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Når enheten låses opp, vises varsler som et banner over toppen av skjermen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"«<xliff:g id="APP_NAME">%1$s</xliff:g>»: alle varsler"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g>: alle varsler"</string> diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml index 0c4c653f89..6ed3adaeaa 100644 --- a/res/values-ne/arrays.xml +++ b/res/values-ne/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"आन्तरिक उपकरण भण्डारण"</item> diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 4845b0f6d0..b7529fde15 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"बज्दैन पनि, भाइब्रेट पनि हुँदैन र वार्तालाप खण्डको तलतिर देखा पर्छ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"फोनको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"डिभाइस अनलक गरिएका बेला सूचना देखाउँदा स्क्रिनको सिरानमा ब्यानरका रूपमा देखाइयोस्"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" का सबै सूचनाहरू"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g> सम्बन्धी सबै सूचनाहरू"</string> <string name="default_notification_assistant" msgid="243718059890346442">"अनुकूल पार्न मिल्ने सूचनाहरू"</string> diff --git a/res/values-nl/arrays.xml b/res/values-nl/arrays.xml index 875e5bcacd..914c5d9bc6 100644 --- a/res/values-nl/arrays.xml +++ b/res/values-nl/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interne apparaatopslag"</item> diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index f8934eb48a..bf34fef04e 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken getoond"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kan overgaan of trillen op basis van de telefooninstellingen"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Toon meldingen als banner bovenaan het scherm wanneer het apparaat is ontgrendeld"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Alle meldingen van <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Alle meldingen van <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Aanpasbare meldingen"</string> diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 15edf892ee..df242a972b 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ ଏବଂ ବାର୍ତ୍ତାଳାପ ବିଭାଗର ନିମ୍ନରେ ଦେଖାଯାଏ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ତଳେ ପ୍ରଦର୍ଶନ କରିଥାଏ। ସର୍ବଦା ନୀରବ।"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ତଳେ ପ୍ରଦର୍ଶନ କରିଥାଏ। ସର୍ବଦା ନୀରବ।"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ତଳେ ପ୍ରଦର୍ଶନ କରିଥାଏ। ସର୍ବଦା ନୀରବ।"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ଡିଭାଇସ୍ ଅନ୍ଲକ୍ ଥିବା ବେଳେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ସ୍କ୍ରିନ୍ର ଉପର ପାର୍ଶ୍ୱରେ ବ୍ୟାନର୍ ଭଳି ଦେଖାଯିବ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"ସମସ୍ତ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ବିଜ୍ଞପ୍ତି"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ସମସ୍ତ <xliff:g id="APP_NAME">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି"</string> diff --git a/res/values-pa/arrays.xml b/res/values-pa/arrays.xml index daa8d5ad5d..7fff6a98c8 100644 --- a/res/values-pa/arrays.xml +++ b/res/values-pa/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ਅੰਦਰੂਨੀ ਡੀਵਾਈਸ ਸਟੋਰੇਜ"</item> diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index dff9ea7bd1..a49da3a25b 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਹੇਠਲੇ ਪਾਸੇ ਦਿਸਦੀਆਂ ਹਨ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"ਡੀਵਾਈਸ ਅਣਲਾਕ ਹੋਣ \'ਤੇ, ਸੂਚਨਾਵਾਂ ਨੂੰ ਸਕ੍ਰੀਨ ਦੇ ਸਿਖਰ \'ਤੇ ਬੈਨਰ ਵਜੋਂ ਦਿਖਾਓ"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਾਮੋਸ਼।"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਾਮੋਸ਼।"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਾਮੋਸ਼।"</string> <string name="notification_switch_label" msgid="8029371325967501557">"ਸਾਰੀਆਂ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ਸੂਚਨਾਵਾਂ"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"ਸਾਰੀਆਂ <xliff:g id="APP_NAME">%1$s</xliff:g> ਸੂਚਨਾਵਾਂ"</string> <string name="default_notification_assistant" msgid="243718059890346442">"ਅਡੈਪਟਿਵ ਸੂਚਨਾਵਾਂ"</string> diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml index d10c2285ca..fdba8bcbab 100644 --- a/res/values-pl/arrays.xml +++ b/res/values-pl/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Wewnętrzna pamięć urządzenia"</item> diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index c60cd4fd5e..ead8bb3e21 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -4004,6 +4004,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Brak dźwięku i wibracji"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Brak dźwięku i wibracji, wyświetla się niżej w sekcji rozmów"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Może włączyć dzwonek lub wibracje w zależności od ustawień telefonu"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Wyświetla się pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Wyświetla się pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Wyświetla się pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Gdy urządzenie jest odblokowane, pokazuj powiadomienia jako pasek u góry ekranu"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Wszystkie powiadomienia z aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Wszystkie powiadomienia z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-pt-rBR/arrays.xml b/res/values-pt-rBR/arrays.xml index e802e57096..b70a8efcfe 100644 --- a/res/values-pt-rBR/arrays.xml +++ b/res/values-pt-rBR/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Armazenamento do dispositivo interno"</item> diff --git a/res/values-pt-rPT/arrays.xml b/res/values-pt-rPT/arrays.xml index 5994f66cac..4bd74a186b 100644 --- a/res/values-pt-rPT/arrays.xml +++ b/res/values-pt-rPT/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Armazenamento de dispositivo interno"</item> diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 3229e98482..444c9a60c1 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Sem som ou vibração e aparece na parte inferior na secção de conversas."</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Pode tocar ou vibrar com base nas definições do telemóvel."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Com o dispositivo desbloqueado, as notificações são apresentadas como uma faixa no topo do ecrã"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"É apresentada abaixo das notificações prioritárias. Sempre silenciosa."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"É apresentada abaixo das notificações prioritárias. Sempre silenciosa."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"É apresentada abaixo das notificações prioritárias. Sempre silenciosa."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Todas as notificações da app \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Todas as notificações da app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notificações adaptáveis"</string> diff --git a/res/values-pt-rPT/strings_group.xml b/res/values-pt-rPT/strings_group.xml new file mode 100644 index 0000000000..44a330afe5 --- /dev/null +++ b/res/values-pt-rPT/strings_group.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">Grupo ID</string> + <string name="refresh_group">Atualizar Grupo</string> + <string name="cancel_refresh_group">Cancelar atualização</string> + <string name="disconnect_group">Desconectar Grupo</string> + <string name="connect_group">Grupo de conexão</string> + <string name="forget_group">Grupo de conexão</string> + <string name="group_previously_connected_screen_title">Grupo previamente conectado</string> + <string name="connected_group">Grupo Conectado</string> + <string name="group_connected_device_media_device_title">Dispositivos Group Media</string> + <string name="group_settings">Grupo</string> + <string name="see_all_available_group">Veja todos os grupos disponíveis</string> + <string name="previous_connected_see_all_groups">Veja todos os grupos</string> + <string name="group_options">Opções de grupo</string> + <string name="group_connected_devices">Dispositivos conectados</string> + <string name="group_active_devices">Dispositivos Ativos</string> + <string name="group_bonded_devices">Dispositivo Ligado</string> + <string name="active">Ativa</string> + <string name="groupaudio_unpair_dialog_title">Esquecer o grupo ?</string> + <string name="groupaudio_unpair_dialog_body">Seu telefone não será mais emparelhado com o conjunto <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">Esquecer Grupo</string> + <string name="group_apply_changes_dialog_title">Aplicar alterações para todos os membros do grupo ?</string> + <string name="group_confirm_dialog_body">Mesmas mudanças aplicáveis a todos os membros do grupo <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">Aplique</string> +</resources> diff --git a/res/values-pt/arrays.xml b/res/values-pt/arrays.xml index e802e57096..b70a8efcfe 100644 --- a/res/values-pt/arrays.xml +++ b/res/values-pt/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Armazenamento do dispositivo interno"</item> diff --git a/res/values-pt/strings_group.xml b/res/values-pt/strings_group.xml new file mode 100644 index 0000000000..44a330afe5 --- /dev/null +++ b/res/values-pt/strings_group.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">Grupo ID</string> + <string name="refresh_group">Atualizar Grupo</string> + <string name="cancel_refresh_group">Cancelar atualização</string> + <string name="disconnect_group">Desconectar Grupo</string> + <string name="connect_group">Grupo de conexão</string> + <string name="forget_group">Grupo de conexão</string> + <string name="group_previously_connected_screen_title">Grupo previamente conectado</string> + <string name="connected_group">Grupo Conectado</string> + <string name="group_connected_device_media_device_title">Dispositivos Group Media</string> + <string name="group_settings">Grupo</string> + <string name="see_all_available_group">Veja todos os grupos disponíveis</string> + <string name="previous_connected_see_all_groups">Veja todos os grupos</string> + <string name="group_options">Opções de grupo</string> + <string name="group_connected_devices">Dispositivos conectados</string> + <string name="group_active_devices">Dispositivos Ativos</string> + <string name="group_bonded_devices">Dispositivo Ligado</string> + <string name="active">Ativa</string> + <string name="groupaudio_unpair_dialog_title">Esquecer o grupo ?</string> + <string name="groupaudio_unpair_dialog_body">Seu telefone não será mais emparelhado com o conjunto <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">Esquecer Grupo</string> + <string name="group_apply_changes_dialog_title">Aplicar alterações para todos os membros do grupo ?</string> + <string name="group_confirm_dialog_body">Mesmas mudanças aplicáveis a todos os membros do grupo <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">Aplique</string> +</resources> diff --git a/res/values-ro/arrays.xml b/res/values-ro/arrays.xml index a8c65450b5..5fc30a9701 100644 --- a/res/values-ro/arrays.xml +++ b/res/values-ro/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Stocare internă pe dispozitiv"</item> diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 229096ec26..0bf04311b0 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -3961,6 +3961,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Fără sunet sau vibrații și apare în partea de jos a secțiunii de conversație"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Poate să sune sau să vibreze, în funcție de setările telefonului"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Când dispozitivul este deblocat, afișează notificările ca un banner în partea de sus a ecranului"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Toate notificările din „<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Toate notificările din <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Notificări adaptive"</string> diff --git a/res/values-ru/arrays.xml b/res/values-ru/arrays.xml index e2ade5d33f..2ce985b2d2 100644 --- a/res/values-ru/arrays.xml +++ b/res/values-ru/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Внутренний накопитель устройства"</item> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 1e128ee99f..aef23a1859 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -4004,6 +4004,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без звука и вибрации"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Без звука или вибрации, появляется в нижней части списка разговоров"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Звонок или вибрация в зависимости от настроек телефона"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Показ уведомлений с низким приоритетом. Всегда без звука."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Показ уведомлений с низким приоритетом. Всегда без звука."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Показ уведомлений с низким приоритетом. Всегда без звука."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"При разблокированном устройстве показывать уведомления в виде баннера в верхней части экрана"</string> <string name="notification_switch_label" msgid="8029371325967501557">"<xliff:g id="APP_NAME">%1$s</xliff:g>: все уведомления"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Показывать все уведомления приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> diff --git a/res/values-si/arrays.xml b/res/values-si/arrays.xml index 207c582417..6f849e1e8f 100644 --- a/res/values-si/arrays.xml +++ b/res/values-si/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"අභ්යන්තර උපාංග ආචයනය"</item> diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 3b8aa8e919..8e6f552578 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"හඬක් හෝ කම්පනයක් නැත"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"හඬක් හෝ කම්පනයක් නැති අතර සංවාද කොටසේ පහළම දිස් වේ"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"දුරකථන සැකසීම් මත පදනම්ව නාද කිරීමට හෝ කම්පනය කිරීමට හැකිය"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශන වෙයි. සැම විටම නිහඬයි."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශන වෙයි. සැම විටම නිහඬයි."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශන වෙයි. සැම විටම නිහඬයි."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"උපාංගය අඟුලු අරිනු ලැබූ විට, තිරයේ මුදුනින් දැනුම්දීම් බැනරයක් ලෙස පෙන්වන්න"</string> <string name="notification_switch_label" msgid="8029371325967501557">"සියලු \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" දැනුම් දීම්"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"සියලු <xliff:g id="APP_NAME">%1$s</xliff:g> දැනුම් දීම්"</string> diff --git a/res/values-sk/arrays.xml b/res/values-sk/arrays.xml index 0c9e75d3a3..600800d37f 100644 --- a/res/values-sk/arrays.xml +++ b/res/values-sk/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Interné úložisko zariadenia"</item> diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 3f699fc281..f717d57c48 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -4006,6 +4006,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Žiadny zvuk ani vibrácie"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Žiadny zvuk ani vibrácie a zobrazuje sa nižšie v sekcii konverzácií"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Zvoní či vibruje podľa nastavení telefónu"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Keď je zariadenie odomknuté, zobrazovať upozornenia ako banner v hornej časti obrazovky"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Všetky upozornenia aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Všetky upozornenia aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-sl/arrays.xml b/res/values-sl/arrays.xml index 58223f08cd..6434393845 100644 --- a/res/values-sl/arrays.xml +++ b/res/values-sl/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Notranji pomnilnik naprave"</item> diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index a4f76837c4..3594a89ee6 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -4007,6 +4007,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Brez zvočnega opozarjanja ali vibriranja, prikaz nižje v razdelku Pogovor."</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev telefona."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kadar je naprava odklenjena, so obvestila prikazana kot pasica na vrhu zaslona."</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Vsa obvestila aplikacije »<xliff:g id="APP_NAME">%1$s</xliff:g>«"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Vsa obvestila aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Prilagodljiva obvestila"</string> diff --git a/res/values-sq/arrays.xml b/res/values-sq/arrays.xml index 2bd9b766e2..e4ff4f7b26 100644 --- a/res/values-sq/arrays.xml +++ b/res/values-sq/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Hapësira e brendshme ruajtëse e pajisjes"</item> diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index a39ba5fbbb..b44f67dad7 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Asnjë tingull ose dridhje"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Asnjë tingull ose dridhje dhe shfaqet më poshtë në seksionin e bisedave"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të telefonit"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kur pajisja është e shkyçur, shfaq njoftimet si një banderolë përgjatë kreut të ekranit"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Të gjitha njoftimet e \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Të gjitha njoftimet e <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-sr/arrays.xml b/res/values-sr/arrays.xml index 8cbd3637ec..f4ea8ab33c 100644 --- a/res/values-sr/arrays.xml +++ b/res/values-sr/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Складиште унутрашњег уређаја"</item> diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 218cbc9533..0d87276f86 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -3958,6 +3958,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без звука и вибрирања"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Без звука и вибрирања и приказује се у наставку одељка за конверзације"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Може да звони или вибрира у зависности од подешавања телефона"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Приказује се испод приоритетних обавештења. Увек нечујно."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Приказује се испод приоритетних обавештења. Увек нечујно."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Приказује се испод приоритетних обавештења. Увек нечујно."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Када је уређај откључан, приказује обавештења као банер у врху екрана"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Сва обавештења апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Сва обавештења апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml index 15ca538565..b05ad454d0 100644 --- a/res/values-sv/arrays.xml +++ b/res/values-sv/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Lagring på intern enhet"</item> diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 82526a686b..6af8c4bb74 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Inga ljud eller vibrationer"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Inga ljud eller vibrationer och visas längre ned bland konversationerna"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Kan ringa eller vibrera beroende på inställningarna på telefonen"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Aviseringar med låg prioritet visas. Alltid tyst."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Aviseringar med låg prioritet visas. Alltid tyst."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Aviseringar med låg prioritet visas. Alltid tyst."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"När enheten är olåst visas aviseringar i en banner högst upp på skärmen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Alla aviseringar från <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Alla aviseringar från <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-sw/arrays.xml b/res/values-sw/arrays.xml index b3ebf18d20..13462fd25c 100644 --- a/res/values-sw/arrays.xml +++ b/res/values-sw/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Kifaa cha hifadhi ya ndani"</item> diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index e5b5f646b2..4497c6f971 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Hakuna sauti wala mtetemo na huonekana upande wa chini katika sehemu ya mazungumzo"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Huenda ikalia au kutetema kulingana na mipangilio ya simu"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Wakati kifaa kimefunguliwa, onyesha arifa kama bango katika sehemu ya juu ya skrini"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Arifa zote za \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Arifa zote za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Arifa Zinazojirekebisha"</string> diff --git a/res/values-ta/arrays.xml b/res/values-ta/arrays.xml index fb62e80e92..67dcb16e62 100644 --- a/res/values-ta/arrays.xml +++ b/res/values-ta/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"சாதன அகச் சேமிப்பு"</item> diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 727ec7b5b9..526a689027 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ஒலி / அதிர்வு இல்லை"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ஒலி / அதிர்வு இல்லாமல் உரையாடல் பிரிவின் கீழ்ப் பகுதியில் தோன்றும்"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"மொபைல் அமைப்புகளின் அடிப்படையில் ஒலிக்கவோ அதிரவோ செய்யும்"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"முக்கியத்துவம் குறைந்த அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"முக்கியத்துவம் குறைந்த அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"முக்கியத்துவம் குறைந்த அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"மொபைல் அன்லாக் செய்யப்பட்ட நிலையிலிருக்கும்போது அறிவிப்புகளைத் திரையின் மேல் பகுதியில் பேனராகக் காட்டும்"</string> <string name="notification_switch_label" msgid="8029371325967501557">"அனைத்து \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" அறிவிப்புகளும்"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"அனைத்து <xliff:g id="APP_NAME">%1$s</xliff:g> அறிவிப்புகளும்"</string> diff --git a/res/values-te/arrays.xml b/res/values-te/arrays.xml index e264c25e44..20bbfe856b 100644 --- a/res/values-te/arrays.xml +++ b/res/values-te/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"అంతర్గత పరికర నిల్వ"</item> diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 5e761495d2..3859cbcb40 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"శబ్దం లేదా వైబ్రేషన్ లేదు, సంభాషణ విభాగం దిగువన కనిపిస్తుంది"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"ఫోన్ సెట్టింగ్ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"పరికరాన్ని అన్లాక్ చేసినప్పుడు స్క్రీన్ పైభాగంలో ఒక బ్యానర్గా నోటిఫికేషన్లను చూపించు"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"తక్కువ ప్రాధాన్యత గల నోటిఫికేషన్లను ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశ్శబ్దంగా ఉంచుతుంది."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"తక్కువ ప్రాధాన్యత గల నోటిఫికేషన్లను ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశ్శబ్దంగా ఉంచుతుంది."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"తక్కువ ప్రాధాన్యత గల నోటిఫికేషన్లను ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశ్శబ్దంగా ఉంచుతుంది."</string> <string name="notification_switch_label" msgid="8029371325967501557">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" నోటిఫికేషన్లన్నీ"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"అన్ని <xliff:g id="APP_NAME">%1$s</xliff:g> నోటిఫికేషన్లు"</string> <string name="default_notification_assistant" msgid="243718059890346442">"అనుకూల నోటిఫికేషన్లు"</string> diff --git a/res/values-th/arrays.xml b/res/values-th/arrays.xml index bc399ab1ea..976ecf6a09 100644 --- a/res/values-th/arrays.xml +++ b/res/values-th/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"ที่จัดเก็บข้อมูลอุปกรณ์ภายใน"</item> diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 44d61dd9ff..e78e7dfb54 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"ไม่มีเสียงหรือการสั่น"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"ไม่มีเสียงหรือการสั่น และปรากฏต่ำลงมาในส่วนการสนทนา"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าโทรศัพท์"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"เมื่ออุปกรณ์ปลดล็อกอยู่ ให้แสดงการแจ้งเตือนเป็นแบนเนอร์ที่ด้านบนของหน้าจอ"</string> <string name="notification_switch_label" msgid="8029371325967501557">"การแจ้งเตือนทั้งหมดของ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"การแจ้งเตือนทั้งหมดของ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-tl/arrays.xml b/res/values-tl/arrays.xml index 0e5344fd42..4e4ce9a173 100644 --- a/res/values-tl/arrays.xml +++ b/res/values-tl/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Storage ng panloob na device"</item> diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 0589f55f35..ab16401ec2 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Walang tunog o pag-vibrate"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Walang tunog o pag-vibrate at lumalabas nang mas mababa sa seksyon ng pag-uusap"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng telepono"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Kapag naka-unlock ang device, ipakita ang mga notification bilang banner sa itaas ng screen"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Lahat ng notification ng \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Lahat ng notification ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-tr/arrays.xml b/res/values-tr/arrays.xml index 29c460d2e1..f17a82d6f9 100644 --- a/res/values-tr/arrays.xml +++ b/res/values-tr/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Dahili cihaz depolaması"</item> diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 267be6196e..b222634197 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Sessiz veya titreşim yok"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Sessizdir veya titreşim yoktur ve görüşme bölümünün altında görünür"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Telefon ayarlarına bağlı olarak zili çalabilir veya titreyebilir"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Öncelikli bildirimlerin altında gösterilir. Her zaman sessiz."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Öncelikli bildirimlerin altında gösterilir. Her zaman sessiz."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Öncelikli bildirimlerin altında gösterilir. Her zaman sessiz."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Cihaz kilitli değilken, bildirimleri ekranın üst kısmında banner olarak göster"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Tüm \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" bildirimleri"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Tüm <xliff:g id="APP_NAME">%1$s</xliff:g> bildirimleri"</string> diff --git a/res/values-uk/arrays.xml b/res/values-uk/arrays.xml index 6200fd12eb..27fc5e71f5 100644 --- a/res/values-uk/arrays.xml +++ b/res/values-uk/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Внутрішня пам\'ять пристрою"</item> diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index d63be1d64f..e860053f8a 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -4004,6 +4004,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Без звуку чи вібрації"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Без звуку чи вібрації, з\'являється нижче в розділі розмов"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Може дзвонити або вібрувати залежно від налаштувань телефона"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Коли пристрій розблоковано, показувати сповіщення як банер угорі екрана"</string> <string name="notification_switch_label" msgid="8029371325967501557">"<xliff:g id="APP_NAME">%1$s</xliff:g>: усі сповіщення"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Усі сповіщення від додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values-ur/arrays.xml b/res/values-ur/arrays.xml index cab4c4b2f7..f018bcb0ee 100644 --- a/res/values-ur/arrays.xml +++ b/res/values-ur/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"آلہ کا داخلی اسٹوریج"</item> diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index d50bbd8931..979e2d64d6 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -3912,6 +3912,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"کوئی آواز یا وائبریشن نہیں"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"کوئی آواز یا وائبریشن نہیں اور گفتگو کے سیکشن میں نیچے ظاہر ہوتا ہے"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"آپ کے آلہ کی ترتیبات کے مطابق وائبریٹ یا گھنٹی بج سکتی ہے"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"آلہ کے غیر مقفل ہو جانے پر، اطلاعات کو اسکرین کے اوپر بینر کے طور پر دکھائيں"</string> <string name="notification_switch_label" msgid="8029371325967501557">"سبھی \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" کی اطلاعات"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"سبھی <xliff:g id="APP_NAME">%1$s</xliff:g> اطلاعات"</string> diff --git a/res/values-uz/arrays.xml b/res/values-uz/arrays.xml index 58bd2de734..e835a5b25f 100644 --- a/res/values-uz/arrays.xml +++ b/res/values-uz/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Ichki xotira qurilmasi"</item> diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 6973cdd76f..6111552394 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Tovush yoki tebranishsiz hamda suhbatlar ruknining pastida chiqadi"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Telefon sozlamalari asosida jiringlashi yoki tebranishi mumkin"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Telefon ochiqligida bildirishnomalar ekranning yuqori qismida banner sifatida chiqadi"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Barcha <xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnomalari"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Barcha <xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnomalari"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Moslashuvchan bildirishnomalar"</string> diff --git a/res/values-vi/arrays.xml b/res/values-vi/arrays.xml index 867438892b..2699c9f075 100644 --- a/res/values-vi/arrays.xml +++ b/res/values-vi/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Bộ nhớ trong của thiết bị"</item> diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 8e055c8ced..9cc185ceec 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -3915,6 +3915,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Không phát âm thanh hoặc rung và xuất hiện phía dưới trong phần cuộc trò chuyện"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Có thể đổ chuông hoặc rung tùy theo chế độ cài đặt trên điện thoại"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Hiện thông báo ở đầu màn hình khi thiết bị đang mở khóa"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string> <string name="notification_switch_label" msgid="8029371325967501557">"Tất cả các thông báo của ứng dụng \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Tất cả thông báo của <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="default_notification_assistant" msgid="243718059890346442">"Thông báo thích ứng"</string> diff --git a/res/values-zh-rCN/arrays.xml b/res/values-zh-rCN/arrays.xml index 3b9dcf5514..acfacff36b 100644 --- a/res/values-zh-rCN/arrays.xml +++ b/res/values-zh-rCN/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"内部存储设备"</item> @@ -589,4 +590,10 @@ <!-- no translation found for rtt_setting_mode:1 (7903019313228349427) --> <!-- no translation found for rtt_setting_mode:2 (8525285145696236811) --> <!-- no translation found for rtt_setting_mode:3 (7725394146877517088) --> + <string-array name="preferred_5g_network_mode_choices"> + <item>NSA 模式</item> + <item>SA 模式</item> + <item>NSA + SA 模式</item> + </string-array> + </resources> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 3a2aa125ef..777d82c6e2 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -3428,6 +3428,8 @@ <string name="contact_discovery_opt_in_dialog_message_no_carrier_defined" msgid="1914894516552445911">"系统会定期将您的联系人电话号码发送给您的运营商。<xliff:g id="EMPTY_LINE"> </xliff:g>此信息可用于识别您的联系人能否使用特定功能,例如视频通话或某些消息传递功能。"</string> + <!-- Enabled 5G Mode title. [CHAR LIMIT=50] --> + <string name="enabled_5g_mode_title">启用5G</string> <string name="preferred_network_type_title" msgid="812509938714590857">"首选网络类型"</string> <string name="preferred_network_type_summary" msgid="8786291927331323061">"LTE(推荐)"</string> <string name="mms_message_title" msgid="6624505196063391964">"彩信"</string> @@ -3640,6 +3642,7 @@ <string name="spatial_audio_title" msgid="6591051622375191603">"空间音频"</string> <string name="dial_pad_tones_title" msgid="3536945335367914892">"拨号键盘提示音"</string> <string name="screen_locking_sounds_title" msgid="5695030983872787321">"屏幕锁定提示音"</string> + <string name="call_connected_tones_title" msgid="1999293510400911558">"通话接通提示音"</string> <string name="charging_sounds_title" msgid="5261683808537783668">"充电提示音和振动"</string> <string name="docking_sounds_title" msgid="5341616179210436159">"基座提示音"</string> <string name="touch_sounds_title" msgid="2200734041857425078">"触摸提示音"</string> @@ -3912,6 +3915,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"不发出提示音,也不振动"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"不发出提示音,也不振动;显示在对话部分的靠下位置"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"可能会响铃或振动(取决于手机设置)"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"显示在优先通知下方。一律不发出提示音。"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"显示在优先通知下方。一律不发出提示音。"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"显示在优先通知下方。一律不发出提示音。"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"当设备处于解锁状态时,在屏幕顶端以横幅形式显示通知"</string> <string name="notification_switch_label" msgid="8029371325967501557">"所有“<xliff:g id="APP_NAME">%1$s</xliff:g>”通知"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"<xliff:g id="APP_NAME">%1$s</xliff:g>的所有通知"</string> @@ -5091,6 +5097,18 @@ <string name="platform_compat_default_disabled_title" msgid="3975847180953793602">"默认停用的应用兼容性变更"</string> <string name="platform_compat_dialog_title_no_apps" msgid="4387656000745989506">"没有可用的应用"</string> <string name="platform_compat_dialog_text_no_apps" msgid="5715226015751055812">"只能修改可调试应用的应用兼容性变更。请安装可调试的应用,然后重试。"</string> + <!-- apn names for carriers --> + <string name="APN_NAME_CMNET">中国移动NET</string> + <string name="APN_NAME_CMWAP">中国移动WAP</string> + <string name="APN_NAME_CMMMS">中国移动彩信设置</string> + <string name="APN_NAME_CUWAP">沃宽带用户手机上网</string> + <string name="APN_NAME_CUNET">沃宽带用户连接互联网</string> + <string name="APN_NAME_CUMMS">联通彩信</string> + <string name="APN_NAME_CUSUPL">联通AGPS</string> + <string name="APN_NAME_CTNET">中国电信互联网设置 CTNET</string> + <string name="APN_NAME_CTWAP">中国电信WAP设置 CTWAP</string> + <string name="APN_NAME_CTLTE">中国电信互联网设置 ctlte</string> + <string name="APN_NAME_CTMMS">中国电信彩信设置 CTWAP</string> <string name="unsupported_setting_summary" product="default" msgid="1085229417771470172">"此手机不支持这项设置"</string> <string name="unsupported_setting_summary" product="tablet" msgid="7402414129786489664">"此平板电脑不支持这项设置"</string> <string name="unsupported_setting_summary" product="device" msgid="3422953459122926833">"此设备不支持这项设置"</string> @@ -5206,7 +5224,9 @@ <string name="mobile_data_settings_title" msgid="3927524078598009792">"移动数据"</string> <string name="mobile_data_settings_summary" msgid="7323978798199919063">"通过移动网络访问数据"</string> <string name="mobile_data_settings_summary_auto_switch" msgid="7851549787645698945">"手机会在进入有效范围时自动切换到此运营商"</string> + <string name="data_preference">"数据偏好设置"</string> <string name="mobile_data_settings_summary_unavailable" msgid="3309106501029928951">"没有可用的 SIM 卡"</string> + <string name="mobile_data_settings_summary_default_data_unavailable">"通话挂断后数据会自动切换到此运营商"</string> <string name="calls_preference" msgid="2166481296066890129">"通话偏好设置"</string> <string name="sms_preference" msgid="7742964962568219351">"短信偏好设置"</string> <string name="calls_and_sms_ask_every_time" msgid="3178743088737726677">"每次都询问"</string> @@ -5580,4 +5600,7 @@ <string name="bluetooth_connect_access_dialog_content" msgid="4336436466468405850">"“<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>”想连接到此手机。\n\n您之前没有连接过“<xliff:g id="DEVICE_NAME_1">%2$s</xliff:g>”。"</string> <string name="bluetooth_connect_access_dialog_negative" msgid="4944672755226375059">"不连接"</string> <string name="bluetooth_connect_access_dialog_positive" msgid="3630561675207269710">"连接"</string> + <string name="prefer_5G_nr_mode_title">首选5G模式</string> + <string name="prefer_vonr_mode_title">打开/关闭 VoNR</string> + <string name="prefer_vonr_mode_summary">打开或者关闭VoNR</string> </resources> diff --git a/res/values-zh-rCN/strings_group.xml b/res/values-zh-rCN/strings_group.xml new file mode 100644 index 0000000000..6dbd4bead2 --- /dev/null +++ b/res/values-zh-rCN/strings_group.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">組號</string> + <string name="refresh_group">刷新组</string> + <string name="cancel_refresh_group">取消刷新</string> + <string name="disconnect_group">断开群组</string> + <string name="connect_group">连接组</string> + <string name="forget_group">忘记组</string> + <string name="group_previously_connected_screen_title">先前连接的组</string> + <string name="connected_group">连接组</string> + <string name="group_settings">组</string> + <string name="previously_connected_group_screen_title">先前连接的组</string> + <string name="previous_connected_see_all_groups">查看所有组</string> + <string name="group_options">组选项</string> + <string name="group_connected_devices">联网设备</string> + <string name="group_active_devices">有源设备</string> + <string name="group_bonded_devices">绑定设备</string> + <string name="active">活性</string> + <string name="groupaudio_unpair_dialog_title">忘记群组 ?</string> + <string name="groupaudio_unpair_dialog_body">您的手机将不再与手机配对 <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">忘记组</string> + <string name="group_apply_changes_dialog_title">是否为所有小组成员应用更改?</string> + <string name="group_confirm_dialog_body">适用于组中所有成员的相同更改 <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button"></string> +</resources> diff --git a/res/values-zh-rHK/arrays.xml b/res/values-zh-rHK/arrays.xml index a06b840d51..4d26ccb7a7 100644 --- a/res/values-zh-rHK/arrays.xml +++ b/res/values-zh-rHK/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"內部裝置儲存空間"</item> diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 5ebad90a1c..e8baf22006 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -3916,6 +3916,9 @@ <string name="notification_conversation_summary_low" msgid="6352818857388412326">"無音效或震動,並在對話部分的較低位置顯示"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"根據手機設定發出鈴聲或震動"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"裝置處於解鎖狀態時,在螢幕頂部以橫額格式顯示通知"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"顯示不重要的通知。一律靜音。"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"顯示不重要的通知。一律靜音。"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"顯示不重要的通知。一律靜音。"</string> <string name="notification_switch_label" msgid="8029371325967501557">"所有「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"所有「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知"</string> <string name="default_notification_assistant" msgid="243718059890346442">"自動調節通知"</string> diff --git a/res/values-zh-rHK/strings_group.xml b/res/values-zh-rHK/strings_group.xml new file mode 100644 index 0000000000..73ca703f1e --- /dev/null +++ b/res/values-zh-rHK/strings_group.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">組號</string> + <string name="refresh_group">刷新组</string> + <string name="cancel_refresh_group">取消刷新</string> + <string name="disconnect_group">断开群组</string> + <string name="connect_group">连接组</string> + <string name="forget_group">忘记组</string> + <string name="group_previously_connected_screen_title">先前连接的组</string> + <string name="connected_group">连接组</string> + <string name="group_settings">组</string> + <string name="previously_connected_group_screen_title">先前连接的组</string> + <string name="previous_connected_see_all_groups">查看所有组</string> + <string name="group_options">组选项</string> + <string name="group_connected_devices">联网设备</string> + <string name="group_active_devices">有源设备</string> + <string name="group_bonded_devices">绑定设备</string> + <string name="active">活性</string> + <string name="groupaudio_unpair_dialog_title">忘记群组 ?</string> + <string name="groupaudio_unpair_dialog_body">您的手机将不再与手机配对 <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">忘记组</string> + <string name="group_apply_changes_dialog_title">是否为所有小组成员应用更改?</string> + <string name="group_confirm_dialog_body">适用于组中所有成员的相同更改 <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">应用</string> +</resources> diff --git a/res/values-zh-rTW/arrays.xml b/res/values-zh-rTW/arrays.xml index 347c447784..36b572fef3 100644 --- a/res/values-zh-rTW/arrays.xml +++ b/res/values-zh-rTW/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"內部裝置儲存空間"</item> diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 57c09d1304..e35bbc6895 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -3914,6 +3914,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"不震動或發出聲音"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"不震動或發出聲音,並顯示在對話區的下方"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"根據手機的設定響鈴或震動"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"顯示在優先通知下方,且一律不發出音效。"</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"顯示在優先通知下方,且一律不發出音效。"</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"顯示在優先通知下方,且一律不發出音效。"</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"裝置處於解鎖狀態時,在螢幕頂端以橫幅形式顯示通知"</string> <string name="notification_switch_label" msgid="8029371325967501557">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的所有通知"</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的所有通知"</string> diff --git a/res/values-zh-rTW/strings_group.xml b/res/values-zh-rTW/strings_group.xml new file mode 100644 index 0000000000..73ca703f1e --- /dev/null +++ b/res/values-zh-rTW/strings_group.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">組號</string> + <string name="refresh_group">刷新组</string> + <string name="cancel_refresh_group">取消刷新</string> + <string name="disconnect_group">断开群组</string> + <string name="connect_group">连接组</string> + <string name="forget_group">忘记组</string> + <string name="group_previously_connected_screen_title">先前连接的组</string> + <string name="connected_group">连接组</string> + <string name="group_settings">组</string> + <string name="previously_connected_group_screen_title">先前连接的组</string> + <string name="previous_connected_see_all_groups">查看所有组</string> + <string name="group_options">组选项</string> + <string name="group_connected_devices">联网设备</string> + <string name="group_active_devices">有源设备</string> + <string name="group_bonded_devices">绑定设备</string> + <string name="active">活性</string> + <string name="groupaudio_unpair_dialog_title">忘记群组 ?</string> + <string name="groupaudio_unpair_dialog_body">您的手机将不再与手机配对 <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">忘记组</string> + <string name="group_apply_changes_dialog_title">是否为所有小组成员应用更改?</string> + <string name="group_confirm_dialog_body">适用于组中所有成员的相同更改 <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">应用</string> +</resources> diff --git a/res/values-zu/arrays.xml b/res/values-zu/arrays.xml index e3bd1fee6e..38bac00a02 100644 --- a/res/values-zu/arrays.xml +++ b/res/values-zu/arrays.xml @@ -214,6 +214,7 @@ <item msgid="8568003268185342352">"SPN"</item> <item msgid="1804537219968457989">"IMSI"</item> <item msgid="3441876902463317017">"GID"</item> + <item>ICCID</item> </string-array> <string-array name="app_install_location_entries"> <item msgid="3771157789865587832">"Isitoreji sedivaysi yangaphakathi"</item> diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 703b34e385..ae13c192c8 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -3913,6 +3913,9 @@ <string name="notification_channel_summary_low" msgid="5549662596677692000">"Awukho umsindo noma ukudlidliza"</string> <string name="notification_conversation_summary_low" msgid="6352818857388412326">"Awukho umsindo noma ukudlidliza futhi ivela ngezansi esigabeni sengxoxo"</string> <string name="notification_channel_summary_default" msgid="3674057458265438896">"Ingase ikhale noma idlidlize kuya ngamasethingi wefoni yakho"</string> + <string name="notification_channel_summary_low_status" msgid="7866565328564018007">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string> + <string name="notification_channel_summary_low_lock" msgid="4009247523075328235">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string> + <string name="notification_channel_summary_low_status_lock" msgid="3668028634045057230">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string> <string name="notification_channel_summary_high" msgid="3411637309360617621">"Uma idivayisi ivuliwe, bonisa izaziso njengesibhengezo ngaphezulu kwesikrini"</string> <string name="notification_switch_label" msgid="8029371325967501557">"Zonke izaziso ze-\"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> <string name="notification_app_switch_label" msgid="4422902423925084193">"Zonke izaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string> diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 0fe13feb3e..feccba4fa0 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -229,6 +229,8 @@ <!-- Do not translate. --> <item>@string/wifi_security_wpa2</item> <!-- Do not translate. --> + <item>@string/wifi_security_owe</item> + <!-- Do not translate. --> <item>@string/wifi_security_none</item> </string-array> @@ -241,6 +243,8 @@ <!-- Do not translate. --> <item>1</item> <!-- Do not translate. --> + <item>4</item> + <!-- Do not translate. --> <item>0</item> </string-array> @@ -352,6 +356,20 @@ <item>5</item> </string-array> + <!-- Bluetooth broadcast pin: Titles for Pin configuration --> + <string-array translatable="false" name="bluetooth_broadcast_pin_config_titles"> + <item>Unencrypted (no security)</item> + <item>4 digit pin (low security)</item> + <item>16 digit pin (high security)</item> + </string-array> + + <!-- Bluetooth broadcast pin: Values for Pin configuration --> + <string-array translatable="false" name="bluetooth_broadcast_pin_config_values"> + <item>0</item> + <item>4</item> + <item>16</item> + </string-array> + <!-- Match this with drawable.wifi_signal. --> <skip /> <!-- Wi-Fi settings. The signal strength a Wi-Fi network has. --> <string-array name="wifi_signal"> @@ -521,12 +539,25 @@ <item>20</item> </string-array> + <string-array name="preferred_5g_network_mode_choices"> + <item>NSA Mode</item> + <item>SA Mode</item> + <item>NSA + SA Mode</item> + </string-array> + + <string-array name="preferred_5g_network_mode_values" translatable="false"> + <item>"1"</item> + <item>"2"</item> + <item>"0"</item> + </string-array> + <!-- MVNO Info used in APN editor --> <string-array name="mvno_type_entries"> <item>None</item> <item translatable="false">SPN</item> <item translatable="false">IMSI</item> <item translatable="false">GID</item> + <item translatable="false">ICCID</item> </string-array> <string-array translatable="false" name="mvno_type_values"> @@ -538,6 +569,8 @@ <item>imsi</item> <!-- Do not translate. --> <item>gid</item> + <!-- Do not translate. --> + <item>iccid</item> </string-array> <!-- Apps on SD installation location options in ApplicationSettings --> @@ -1503,6 +1536,39 @@ </string-array> + <!-- String arrays for PrimaryCard LW feature --> + <string-array name="preferred_network_mode_gsm_wcdma_choices"> + <item>GSM only</item> + <item>WCDMA only</item> + <item>GSM/WCDMA preferred</item> + </string-array> + <string-array name="preferred_network_mode_gsm_wcdma_values" translatable="false"> + <item>"1"</item> + <item>"2"</item> + <item>"0"</item> + </string-array> + + <string-array name="enabled_networks_gsm_wcdma_choices"> + <item>@string/network_2G_only</item> + <item>@string/network_3G_only</item> + <item>@string/network_2G_3G_preferred</item> + </string-array> + <string-array name="enabled_networks_gsm_wcdma_values" translatable="false"> + <item>"1"</item> + <item>"2"</item> + <item>"0"</item> + </string-array> + + <!-- String arrays for Subsidy Lock feature --> + <string-array name="enabled_networks_subsidy_locked_choices"> + <item>LTE Only</item> + <item>GSM/WCDMA/LTE</item> + </string-array> + <string-array name="enabled_networks_subsidy_locked_values" translatable="false"> + <item>"11"</item> + <item>"9"</item> + </string-array> + <!-- WiFi calling mode array --> <string-array name="wifi_calling_mode_summaries" translatable="false"> <item>@string/wifi_calling_mode_wifi_preferred_summary</item> @@ -1510,12 +1576,27 @@ <item>@string/wifi_calling_mode_wifi_only_summary</item> </string-array> + <!-- WiFi calling mode array with ims preferred --> + <string-array name="wifi_calling_mode_summaries_with_ims_preferred" translatable="false"> + <item>@string/wifi_calling_mode_ims_preferred_summary</item> + <item>@string/wifi_calling_mode_wifi_preferred_summary</item> + <item>@string/wifi_calling_mode_cellular_preferred_summary</item> + <item>@string/wifi_calling_mode_wifi_only_summary</item> + </string-array> + <!-- WiFi calling mode array without wifi only mode --> <string-array name="wifi_calling_mode_summaries_without_wifi_only" translatable="false"> <item>@string/wifi_calling_mode_wifi_preferred_summary</item> <item>@string/wifi_calling_mode_cellular_preferred_summary</item> </string-array> + <!-- WiFi calling mode array without wifi only mode with ims preferred --> + <string-array name="wifi_calling_mode_summaries_without_wifi_only_with_ims_preferred" translatable="false"> + <item>@string/wifi_calling_mode_ims_preferred_summary</item> + <item>@string/wifi_calling_mode_wifi_preferred_summary</item> + <item>@string/wifi_calling_mode_cellular_preferred_summary</item> + </string-array> + <!-- Carrier variant of Enhaced 4G LTE Mode title. [CHAR LIMIT=NONE] --> <string-array name="enhanced_4g_lte_mode_title_variant"> <!-- 0: Default --> @@ -1596,4 +1677,31 @@ <item>2</item> <item>4</item> </string-array> + <!--String arrays for showing user PLMN list --> + <string-array name="uplmn_prefer_network_mode_td_choices"> + <item>GSM</item> + <item>TDSCDMA</item> + <item>LTE</item> + <item>Triple mode</item> + </string-array> + + <string-array name="uplmn_prefer_network_mode_w_choices"> + <item>GSM</item> + <item>WCDMA</item> + <item>LTE</item> + <item>Triple mode</item> + </string-array> + + <string-array name="uplmn_prefer_network_mode_values"> + <item>"0"</item> + <item>"1"</item> + <item>"2"</item> + <item>"3"</item> + </string-array> + + <string-array name="uplmn_cu_mcc_mnc_values"> + <item>"46001"</item> + <item>"46006"</item> + <item>"46009"</item> + </string-array> </resources> diff --git a/res/values/config.xml b/res/values/config.xml index 987eaf9725..b470050c45 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -455,6 +455,12 @@ <!-- Whether or not extra preview panels should be used for screen zoom setting. --> <bool name="config_enable_extra_screen_zoom_preview">true</bool> + <!-- Whether to add AGPS parameter settings --> + <bool name="config_agps_enabled">false</bool> + + <!-- Whether to support CT PA requirement or not --> + <bool name="config_support_CT_PA">false</bool> + <!-- Slice Uri to query nearby devices. --> <string name="config_nearby_devices_slice_uri" translatable="false">content://com.google.android.gms.nearby.fastpair/device_status_list_item</string> @@ -557,4 +563,8 @@ <!-- Whether to aggregate for network selection list--> <bool name="config_network_selection_list_aggregation_enabled">false</bool> + + <!-- Playing tone setting --> + <bool name="config_show_connect_tone_ui">false</bool> + <integer name="config_default_tone_after_connected">0</integer> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 300b3a4201..ee29b88e5b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -14,6 +14,10 @@ limitations under the License. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Strings for the smart DDS switch toggle in developer options --> + <string name="smart_dds_switch">Smart DDS switch</string> + <string name="smart_dds_switch_wait_summary">Please wait...</string> + <string name="smart_dds_switch_summary">Allow modem to intelligently switch the DDS.</string> <!-- Strings for Dialog yes button --> <string name="yes">"Yes"</string> <!-- Strings for Dialog no button --> @@ -198,6 +202,15 @@ <!-- Bluetooth broadcasting settings, option to enable/disable broadcasting --> <string name="bluetooth_broadcasting">Broadcasting</string> + <!-- Bluetooth broadcast enable/disable --> + <string name="bluetooth_broadcast">Broadcast</string> + + <!-- Bluetooth broadcast settings --> + <string name="bluetooth_broadcast_pin_configure">Broadcast: Bluetooth Privacy Code</string> + + <!-- Bluetooth broadcast settings --> + <string name="bluetooth_broadcast_pin_configure_dialog">Broadcast: Configure Bluetooth Privacy Code</string> + <!--Bluetooth settings screen, summary text for Bluetooth device with no name --> <string name="bluetooth_device">Unnamed Bluetooth device</string> <!--Bluetooth settings screen, text that appears in heading bar when scanning for devices --> @@ -346,6 +359,8 @@ <string name="connected_device_other_device_title">Other devices</string> <!-- Title for connected device group [CHAR LIMIT=none]--> <string name="connected_device_saved_title">Saved devices</string> + <!-- Title for connected TWS device group [CHAR LIMIT=none]--> + <string name="connected_tws_device_saved_title">Saved Earbuds</string> <!-- Summary for preference to add a device [CHAR LIMIT=none]--> <string name="connected_device_add_device_summary">Bluetooth will turn on to pair</string> <!-- Title for other connection preferences [CHAR LIMIT=none]--> @@ -1978,6 +1993,8 @@ <string name="wifi_display_settings_title">Cast</string> <!-- Wifi Display settings. The keywords of the setting. [CHAR LIMIT=NONE] --> <string name="keywords_wifi_display_settings">mirror</string> + <!-- Checkbox title for enabling particual wifi as shared --> + <string name="share_this_wifi">Extend Wi-Fi coverage</string> <!-- Wifi Display settings. The title of a menu item to enable wireless display [CHAR LIMIT=40] --> <string name="wifi_display_enable_menu_item">Enable wireless display</string> <!-- Wifi Display settings. Text that appears when scanning for devices is finished and no nearby device was found [CHAR LIMIT=40]--> @@ -2026,6 +2043,8 @@ <string name="wifi_band_5ghz">5 GHz</string> <!-- Wifi 6GHz is used as a universal identifier for 6GHz band [CHAR LIMIT=40] --> <string name="wifi_band_6ghz">6 GHz</string> + <!-- Wifi Internal 60GHz as an universal identifier for 60GHz band --> + <string name="wifi_band_60ghz">60 GHz</string> <!-- Wifi Sign in text for button [CHAR LIMIT = 40]--> <string name="wifi_sign_in_button_text">Sign in</string> <!-- Text for button to go to Wifi venue information webpage when Wifi is a captive portal [CHAR LIMIT=40]--> @@ -2290,8 +2309,14 @@ <string name="wifi_ap_choose_2G">2.4 GHz Band</string> <!-- Label for the radio button to only choose wifi ap 5GHz band --> <string name="wifi_ap_choose_5G">5.0 GHz Band</string> + <!-- Label for the radio button to only choose wifi ap 6GHz band --> + <string name="wifi_ap_choose_6G">6.0 GHz Band</string> + <!-- Label for the radio button to choose both wifi ap band (2.4GHz + 5GHz) --> + <string name="wifi_ap_choose_vendor_dual_band">Dual Band</string> <!-- Label for the radio button to prefer 5GHz wifi ap band [CHAR LIMIT=80]--> <string name="wifi_ap_prefer_5G">5.0 GHz Band preferred</string> + <!-- Label for the radio button to prefer 6GHz wifi ap band [CHAR LIMIT=80]--> + <string name="wifi_ap_prefer_6G">6.0 GHz Band preferred</string> <!-- Label for adding to the list of selected bands when 2.4 GHz is selected --> <string name="wifi_ap_2G">2.4 GHz</string> <!-- Label for adding to the list of selected bands when 5.0 GHz is selected --> @@ -2623,12 +2648,6 @@ <string name="wifi_hotspot_auto_off_title">Turn off hotspot automatically</string> <!-- Summary for the toggle to turn off hotspot automatically [CHAR LIMIT=NONE]--> <string name="wifi_hotspot_auto_off_summary">When no devices are connected</string> - <!-- Title for the toggle to enable/disable the maximize compatibility [CHAR LIMIT=NONE]--> - <string name="wifi_hotspot_maximize_compatibility">Extend compatibility</string> - <!-- Summary for the toggle to show the maximize compatibility warning message in single AP device [CHAR LIMIT=NONE]--> - <string name="wifi_hotspot_maximize_compatibility_single_ap_summary">Helps other devices find this hotspot. Reduces hotspot connection speed.</string> - <!-- Summary for the toggle to show the maximize compatibility warning message in dual AP device [CHAR LIMIT=NONE]--> - <string name="wifi_hotspot_maximize_compatibility_dual_ap_summary">Helps other devices find this hotspot. Increases battery usage.</string> <!-- Summary text when turning hotspot on --> <string name="wifi_tether_starting">Turning hotspot on\u2026</string> @@ -2776,28 +2795,61 @@ <item>@*android:string/wfc_mode_cellular_preferred_summary</item> <item>@*android:string/wfc_mode_wifi_only_summary</item> </string-array> + <string-array name="wifi_calling_mode_choices_with_ims_preferred" translatable="false"> + <item>@*android:string/wfc_mode_ims_preferred_summary</item> + <item>@*android:string/wfc_mode_wifi_preferred_summary</item> + <item>@*android:string/wfc_mode_cellular_preferred_summary</item> + <item>@*android:string/wfc_mode_wifi_only_summary</item> + </string-array> <string-array name="wifi_calling_mode_choices_v2"> <item>Wi-Fi</item> <item>Mobile</item> <item>Wi-Fi only</item> </string-array> + <string-array name="wifi_calling_mode_choices_v2_with_ims_preferred"> + <item>Ims Preferred</item> + <item>Wi-Fi</item> + <item>Mobile</item> + <item>Wi-Fi only</item> + </string-array> <string-array name="wifi_calling_mode_values" translatable="false"> <item>"2"</item> <item>"1"</item> <item>"0"</item> </string-array> + <string-array name="wifi_calling_mode_values_with_ims_preferred" translatable="false"> + <item>"10"</item> + <item>"2"</item> + <item>"1"</item> + <item>"0"</item> + </string-array> <string-array name="wifi_calling_mode_choices_without_wifi_only" translatable="false"> <item>@*android:string/wfc_mode_wifi_preferred_summary</item> <item>@*android:string/wfc_mode_cellular_preferred_summary</item> </string-array> + <string-array name="wifi_calling_mode_choices_without_wifi_only_with_ims_preferred" translatable="false"> + <item>@*android:string/wfc_mode_ims_preferred_summary</item> + <item>@*android:string/wfc_mode_wifi_preferred_summary</item> + <item>@*android:string/wfc_mode_cellular_preferred_summary</item> + </string-array> <string-array name="wifi_calling_mode_choices_v2_without_wifi_only"> <item>Wi-Fi</item> <item>Mobile</item> </string-array> + <string-array name="wifi_calling_mode_choices_v2_without_wifi_only_with_ims_preferred"> + <item>Ims Preferred</item> + <item>Wi-Fi</item> + <item>Mobile</item> + </string-array> <string-array name="wifi_calling_mode_values_without_wifi_only" translatable="false"> <item>"2"</item> <item>"1"</item> </string-array> + <string-array name="wifi_calling_mode_values_without_wifi_only_with_ims_preferred" translatable="false"> + <item>"10"</item> + <item>"2"</item> + <item>"1"</item> + </string-array> <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]--> <string name="wifi_calling_mode_wifi_preferred_summary">If Wi\u2011Fi is unavailable, use mobile network</string> @@ -2805,6 +2857,8 @@ <string name="wifi_calling_mode_cellular_preferred_summary">If mobile network is unavailable, use Wi\u2011Fi</string> <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]--> <string name="wifi_calling_mode_wifi_only_summary">Call over Wi\u2011Fi. If Wi\u2011Fi is lost, call will end.</string> + <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]--> + <string name="wifi_calling_mode_ims_preferred_summary">If LTE and Wi\u2011Fi are unavailabe, use 2G/3G</string> <!-- Wi-Fi Calling settings. Text displayed when Wi-Fi Calling is off --> <string name="wifi_calling_off_explanation">When Wi-Fi calling is on, your phone can route calls via Wi-Fi networks or your carrier\u2019s network, depending on your preference and which signal is stronger. Before turning on this feature, check with your carrier regarding fees and other details.<xliff:g id="additional_text" example="Learn More">%1$s</xliff:g></string> @@ -8023,6 +8077,8 @@ the carrier to determine if your contacts support enhanced features, such as video calling and RCS messaging. [CHAR LIMIT=NONE]--> <string name="contact_discovery_opt_in_dialog_message_no_carrier_defined">Your contacts\u2019 phone numbers will be periodically sent to your carrier.<xliff:g id="empty_line" example=" ">\n\n</xliff:g>This info identifies whether your contacts can use certain features, like video calls or some messaging features.</string> + <!-- Enabled 5G Mode title. [CHAR LIMIT=50] --> + <string name="enabled_5g_mode_title">Enable 5G</string> <!-- Preferred network type title. [CHAR LIMIT=50] --> <string name="preferred_network_type_title">Preferred network type</string> <!-- Preferred network type summary. [CHAR LIMIT=100] --> @@ -8481,6 +8537,9 @@ <!-- Sound: Other sounds: Title for the option enabling touch sounds for dial pad tones. [CHAR LIMIT=30] --> <string name="dial_pad_tones_title">Dial pad tones</string> + <!-- Sound: Other sounds: Title for the option enabling tones after call connected. [CHAR LIMIT=30] --> + <string name="call_connected_tones_title">Call connected tones</string> + <!-- Sound: Other sounds: Title for the option enabling touch sounds for screen locking sounds. [CHAR LIMIT=30] --> <string name="screen_locking_sounds_title">Screen locking sound</string> @@ -12319,6 +12378,19 @@ <!-- Part of a message for an empty state screen. A user will see this message if they try to use a certain feature, but the feature was turned off so it won't slow down their phone. [CHAR LIMIT=NONE] --> <string name="disabled_feature_reason_slow_down_phone">This feature has been turned off because it slows down your phone</string> + <!-- apn names for carriers --> + <string name="APN_NAME_CMNET">CMNET</string> + <string name="APN_NAME_CMWAP">CMWAP</string> + <string name="APN_NAME_CMMMS">CMMMS</string> + <string name="APN_NAME_CUWAP">China Unicom WAP</string> + <string name="APN_NAME_CUNET">China Unicom NET</string> + <string name="APN_NAME_CUMMS">China Unicom MMS</string> + <string name="APN_NAME_CUSUPL">China Unicom AGPS</string> + <string name="APN_NAME_CTNET">CTNET</string> + <string name="APN_NAME_CTWAP">CTWAP</string> + <string name="APN_NAME_CTLTE">ctlte</string> + <string name="APN_NAME_CTMMS">CTWAP</string> + <!-- UI debug setting: preference title - show all crash dialogs [CHAR LIMIT=60] --> <string name="show_first_crash_dialog">Always show crash dialog</string> <!-- UI debug setting: preference summary - describes the behavior of showing a dialog every time an app crashes [CHAR LIMIT=NONE] --> @@ -12635,6 +12707,8 @@ <!-- Text for Network mode 5g recommended [CHAR LIMIT=NONE] --> <string name="network_5G_recommended">5G (recommended)</string> + <!-- Text for Network 5g lte[CHAR LIMIT=NONE] --> + <string name="network_5G_lte" translatable="false">5G</string> <!-- Text for Network lte [CHAR LIMIT=NONE] --> <string name="network_lte_pure" translatable="false">LTE</string> <!-- Text for Network 4g [CHAR LIMIT=NONE] --> @@ -12655,6 +12729,12 @@ <string name="network_world_mode_cdma_lte" translatable="false">LTE / CDMA</string> <!-- Text for Network world mode GSM LTE [CHAR LIMIT=NONE] --> <string name="network_world_mode_gsm_lte" translatable="false">LTE / GSM / UMTS</string> + <!-- Text for Network 3g only [CHAR LIMIT=NONE] --> + <string name="network_2G_3G_preferred" translatable="false">2G/3G preferred</string> + <!-- Text for Network 3g only [CHAR LIMIT=NONE] --> + <string name="network_3G_only" translatable="false">3G only</string> + <!-- Text for Network 2g only [CHAR LIMIT=NONE] --> + <string name="network_2G_only" translatable="false">2G only</string> <!-- Available networks screen title/heading [CHAR LIMIT=NONE] --> <string name="label_available">Available networks</string> @@ -12689,8 +12769,17 @@ <!-- Mobile network setting screen, summary of Mobile data switch preference when the network is unavailable, the preference selection will be disabled. [CHAR LIMIT=NONE] --> <string name="mobile_data_settings_summary_unavailable">No SIM card available</string> + <!-- Mobile network setting screen, summary of Mobile data switch preference of default data + SUB when the call is ongoing on non-default data SUB, the preference selection will be + disabled. [CHAR LIMIT=NONE] --> + <string name="mobile_data_settings_summary_default_data_unavailable"> + Data will automatically switch to this carrier after call ends + </string> <!-- Mobile network settings screen, title of item showing the name of the default subscription + that will be used for data. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] --> + <string name="data_preference">Data preference</string> + <!-- Mobile network settings screen, title of item showing the name of the default subscription that will be used for calls. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] --> <string name="calls_preference">Calls preference</string> <!-- Mobile network settings screen, title of item showing the name of the default subscription @@ -12881,6 +12970,13 @@ <!-- Available networks screen, summary when button disallowed due to permanent automatic mode [CHAR LIMIT=NONE] --> <string name="manual_mode_disallowed_summary">Unavailable when connected to <xliff:g id="carrier" example="verizon">%1$s</xliff:g></string> + <string name="select_sim_card">Select SIM card</string> + <string name="qtifeedback_settings_title">Hardware Feedback</string> + <string name="qtifeedback_settings_subtitle">Qualcomm Technologies, Inc Reporting</string> + <string name="qtifeedback_intent_action">com.qti.smq.snapdragonSettings</string> + <string name="qtifeedback_package">com.qti.smq.Feedback</string> + <string name="qtifeedback_activity">com.qti.smq.ui.QtiFeedbackActivity</string> + <!-- See more items in contextual homepage [CHAR LIMIT=30]--> <string name="see_more">See more</string> <!-- See less items in contextual homepage [CHAR LIMIT=30]--> @@ -13085,6 +13181,8 @@ <!-- Summary for low storage slice. [CHAR LIMIT=NONE] --> <string name="low_storage_summary">Storage is low. <xliff:g id="percentage" example="54%">%1$s</xliff:g> used - <xliff:g id="free_space" example="32GB">%2$s</xliff:g> free</string> + <string name="reset_default_apn_failed">Failed to reset default apn</string> + <!-- Label for button in contextual card feedback dialog for users to send feedback [CHAR LIMIT=30] --> <string name="contextual_card_feedback_send">Send feedback</string> <!-- String for contextual card feedback dialog [CHAR LIMIT=NONE] --> @@ -13143,6 +13241,11 @@ <!-- Warn the user that the phone may share its location with the carrier. [CHAR LIMIT=NONE] --> <string name="wfc_disclaimer_location_desc_text">Your carrier may collect your location when you use this service for emergency calls.\n\nVisit your carrier\u2019s privacy policy for details.</string> + <!-- String for about phone --> + <string name="ram_total_size">RAM total size</string> + <string name="rom_total_size">ROM total size</string> + <string name="software_version">Software version</string> + <string name="model_hardware_summary">Model:%1$s; Hardware:%2$s</string> <!-- Message for forget passpoint dialog [CHAR LIMIT=none] --> <string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string> @@ -13303,6 +13406,19 @@ <!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] --> <string name="rtt_settings_always_visible"></string> + <!-- Strings for user PLMN setting. [CHAR LIMIT=NONE] --> + <string name="uplmn_settings_title">User Controlled PLMN</string> + <string name="network_id">Network ID</string> + <string name="dialog_network_priority_title">Priority</string> + <string name="uplmn_list_setting_add_plmn">New PLMN</string> + <string name="gsm_umts_network_preferences_title">Network Mode</string> + <string name="gsm_umts_network_preferences_dialogtitle">Network Mode</string> + <string name="voicemail_number_not_set"><Not set></string> + <string name="reading_settings">Reading settings\u2026</string> + <string name="updating_settings">Updating settings\u2026</string> + <string name="uplmn_settings_summary">Define the preferred PLMNs in priority order</string> + <string name="plmn_failure_radio_off">Selecting network failure due to radio OFF or unavailable</string> + <!-- Button label to stop casting on media device. [CHAR LIMIT=40 --> <string name="media_output_panel_stop_casting_button">Stop casting</string> @@ -13636,4 +13752,14 @@ <string name="bluetooth_connect_access_dialog_negative">Don\u2019t connect</string> <!-- Strings for Dialog connect button --> <string name="bluetooth_connect_access_dialog_positive">Connect</string> + <!-- Title of selecting NR mode in developer options settings [CHAR LIMIT=NONE]--> + <string name="prefer_5G_nr_mode_title">Prefer 5G NR Mode</string> + <!-- Summary of selecting NR mode in developer options settings [CHAR LIMIT=NONE]--> + <string name="prefer_5G_nr_mode_summary">Enable 5G if you want to select 5G mode</string> + <!-- String for NSA, NSA, NSA+SA [CHAR LIMIT=NONE]--> + <string name="nr_nsa_sa">NSA + SA</string> + <string name="nr_nsa">NSA</string> + <string name="nr_sa">SA</string> + <string name="prefer_vonr_mode_title">Enable/Disable VoNR</string> + <string name="prefer_vonr_mode_summary">Enable or disable VoNR</string> </resources> diff --git a/res/values/strings_ba.xml b/res/values/strings_ba.xml new file mode 100644 index 0000000000..0673d1efbf --- /dev/null +++ b/res/values/strings_ba.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="bluetooth_search_broadcasters">Search Broadcasters</string> + <string name="bluetooth_bcast_rcvr_device_name">Broadcast receiver</string> + <string name="bluetooth_source_selection_options_detail_title"> + Configure Broadcast receiver</string> + <string name="bluetooth_source_selection_options_detail" product="default">Broadcast source " + <xliff:g id="device_name">%1$s</xliff:g> is being added to receiver. + (Leave Broadcast PIN blank if + It is Unencrypted broadcast Source)"</string> + <string name="bluetooth_grp_source_selection_options_detail" product="default"> + Broadcast source " + <xliff:g id="device_name">%1$s</xliff:g> is being added to the connected group members. + (Leave Broadcast PIN blank if It is Unencrypted broadcast Source)"</string> + <string name="bluetooth_col_source_selection_options_detail" product="default"> + Broadcast source " + <xliff:g id="device_name">%1$s</xliff:g> is being added to receiver"</string> + <string name="bluetooth_col_grp_source_selection_options_detail" product="default"> + Broadcast source " + <xliff:g id="device_name">%1$s</xliff:g> is being added to the connected + group members."</string> + <string name="bluetooth_search_bcast_sources_pref_title">Search Broadcast + sources</string> + <string name="bluetooth_scan_source_title">Scan for Broadcast sources + for</string> + <string name="added_sources_title">Broadcast sources</string> + <string name="add_source_button_text">Add Broadcast Source</string> + <string name="update_sourceinfo_btn_txt">Update Source Info</string> + <string name="remove_sourceinfo_btn_txt">Remove Source Info</string> + <string name="source_info_details_title">Broadcast source Information</string> + <string name="bcast_source_info_sid">Broadcast Source Id</string> + <string name="bcast_source_address">Broadcast Source Device</string> + <string name="bcast_source_enc_status">Encryption Status</string> + <string name="bcast_source_metadata">Metadata</string> + <string name="bcast_metadata_sync_state">Metadata Sync status</string> + <string name="bcast_enable_metadata_sync">Metadata sync</string> + <string name="bcast_audio_sync_state">Audio Sync status</string> + <string name="bcast_enable_audio_sync">Audio sync</string> + <string name="bcast_update_code_title">Update Broadcast code</string> + <string name="bcast_update_code_summary">Add broadcast code here, and click + Update Broadcast Source Info</string> + <string name="bcast_info_update">Update Source Info</string> + <string name="added_sources_dashboard_title">Added Broadcast Sources</string> + <string name="bcast_sink_volume_title">Broadcast sink volume</string> + <string name="group_update_source_title">Update to all Group members?</string> + <string name="group_update_source_message">Do you want to Update this source + in all group members?</string> + <string name="group_remove_source_title">Remove from all Group members?</string> + <string name="group_remove_source_message">Do you want to remove this Broadcast source from + all group members?</string> + <string name="bluetooth_source_selection_error_message">Invalid Broadcast + source selected for Receiver + <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="bluetooth_source_selection_error_src_unavail_message">selected + source for receiver + <xliff:g id="device_name">%1$s</xliff:g>. is Unavailable </string> + <string name="bluetooth_source_addition_error_message">Couldn\'t add the + Broadcast source to receiver + <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="bluetooth_source_dup_addition_error_message">Couldn\'t add + the Broadcast + source to receiver <xliff:g id="device_name">%1$s</xliff:g> + . Duplicate source</string> + <string name="bluetooth_source_no_empty_slot_error_message">Consider removing + some source + from receiver <xliff:g id="device_name">%1$s</xliff:g>. No empty slot</string> + <string name="bluetooth_source_update_error_message"> + Couldn\'t update Broadcast source + details of receiver <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="bluetooth_source_update_invalid_group_op"> + Couldn\'t update the Broadcast + Source from <xliff:g id="device_name">%1$s</xliff:g>.Invalid Group operation, + Try non-Group operation</string> + <string name="bluetooth_source_update_invalid_src_id"> + Couldn\'t update the Broadcast Source from + <xliff:g id="device_name">%1$s</xliff:g>.Invalid Source Id</string> + <string name="bluetooth_source_setpin_error_message"> + Couldn\'t update the Broadcast PIN code for + <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="bluetooth_source_removal_error_message"> + Couldn\'t remove the Broadcast Source from + <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="bluetooth_source_remove_invalid_group_op"> + Couldn\'t remove the Broadcast Source from + <xliff:g id="device_name">%1$s</xliff:g>.Invalid Group operation, + Try non-Group operation</string> + <string name="bluetooth_source_remove_invalid_src_id"> + Couldn\'t remove the Broadcast Source from + <xliff:g id="device_name">%1$s</xliff:g>.Invalid Source Id</string> + <string name="bluetooth_source_added_message">Broadcast source added + successfully to receiver + <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string-array name="bcast_channel_selection"> + </string-array> + <string name="metadata_synced">Metadata Synced</string> + <string name="metadata_not_synced">Metadata not synced</string> + <string name="audio_synced">Audio Synced</string> + <string name="audio_not_synced">Audio not synced</string> +</resources> diff --git a/res/values/strings_group.xml b/res/values/strings_group.xml new file mode 100644 index 0000000000..c848159604 --- /dev/null +++ b/res/values/strings_group.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="group_id">Group ID</string> + <string name="refresh_group">Refresh Group</string> + <string name="cancel_refresh_group">Cancel Refresh</string> + <string name="disconnect_group">Disconnect Group</string> + <string name="connect_group">Connect Group</string> + <string name="forget_group">Forget Group</string> + <string name="group_previously_connected_screen_title">Previously Connected group</string> + <string name="group_help_url_previously_connected_devices" translatable="false"></string> + <string name="group_settings">Group</string> + <string name="previously_connected_group_screen_title">Previously Connected group</string> + <string name="help_url_previously_connected_group_devices" translatable="false"></string> + <string name="previous_connected_see_all_groups">See all groups</string> + <string name="group_options">Group options</string> + <string name="group_connected_devices">Connected Devices</string> + <string name="group_active_devices">Active Devices</string> + <string name="group_bonded_devices">Bonded Device</string> + <string name="group_help_url_all_connected_devices" translatable="false"></string> + <string name="active">Active</string> + <string name="groupaudio_unpair_dialog_title">Forget Group ?</string> + <string name="groupaudio_unpair_dialog_body">Your phone will + no longer be paired with group <xliff:g id="setid">%1$s</xliff:g></string> + <string name="groupaudio_unpair_dialog_forget_confirm_button">Forget Group</string> + <string name="add_source_group">Add Broadcast Source to Group</string> + <string name="group_apply_changes_dialog_title">Apply changes for all group members ?</string> + <string name="group_confirm_dialog_body">Same changes applicable for all members of group <xliff:g id="groupid">%1$s</xliff:g></string> + <string name="group_confirm_dialog_apply_button">Apply</string> +</resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 8402dec73c..2c7e6fcba9 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -926,4 +926,11 @@ parent="@*android:style/TextAppearance.DeviceDefault"> <item name="android:textColor">?android:attr/textColorSecondary</item> </style> + + <style name="GroupOptionsButton" parent="android:Widget.DeviceDefault.Button"> + <item name="android:drawablePadding">4dp</item> + <item name="android:layout_marginEnd">8dp</item> + <item name="android:paddingTop">20dp</item> + <item name="android:paddingBottom">20dp</item> + </style> </resources> diff --git a/res/xml/bcast_source_info_details_fragment.xml b/res/xml/bcast_source_info_details_fragment.xml new file mode 100644 index 0000000000..5a5eef15c3 --- /dev/null +++ b/res/xml/bcast_source_info_details_fragment.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2017 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/source_info_details_title"> + + <PreferenceCategory + android:key="broadcast_source_details_category"> + + <Preference + android:key="broadcast_si_sourceId" + android:title="@string/bcast_source_info_sid" + android:summary="00" + /> + + <Preference + android:key="broadcast_si_source_address" + android:title="@string/bcast_source_address" + android:summary="00:00:00:00:00:00" + /> + + <Preference + android:key="broadcast_si_metadata_state" + android:title="@string/bcast_metadata_sync_state" + android:summary="NONE" + /> +<!-- + <Preference + android:key="broadcast_si_audio_state" + android:title="@string/bcast_audio_sync_state" + android:summary="NONE" + /> +--> + + <MultiSelectListPreference + android:title="@string/bcast_audio_sync_state" + android:key="broadcast_si_audio_state" + android:persistent="false" + android:entries="@array/bcast_channel_selection" + android:entryValues="@array/bcast_channel_selection" + style="@style/SettingsMultiSelectListPreference" + /> + + <Preference + android:key="broadcast_si_encryption_state" + android:title="@string/bcast_source_enc_status" + android:summary="No encryption key" + /> + + <Preference + android:key="broadcast_si_metadata" + android:title="@string/bcast_source_metadata" + android:summary="Music" + /> +<!-- + <SwitchPreference + android:key="broadcast_si_enable_metadata_sync" + android:title="@string/bcast_enable_metadata_sync" + android:defaultValue="false" + settings:allowDividerAbove="true"/> +--> + <SwitchPreference + android:key="broadcast_si_enable_audio_sync" + android:title="@string/bcast_enable_audio_sync" + android:defaultValue="false" /> + + <EditTextPreference + android:key="update_broadcast_code" + android:title="@string/bcast_update_code_title" + android:summary="@string/bcast_update_code_summary" + android:inputType="text" + android:ems="16" + android:defaultValue="" /> + + <com.android.settingslib.widget.ActionButtonsPreference + android:key="bcast_si_update_button" + android:title="@string/bcast_info_update" + settings:allowDividerBelow="true"/> + + </PreferenceCategory> + </PreferenceScreen> diff --git a/res/xml/bluetooth_broadcast_pin_config.xml b/res/xml/bluetooth_broadcast_pin_config.xml new file mode 100644 index 0000000000..56a89600c5 --- /dev/null +++ b/res/xml/bluetooth_broadcast_pin_config.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) 2021, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="8dp"> + <TextView + android:id="@+id/bluetooth_broadcast_current_pin" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <RadioGroup + android:id="@+id/bluetooth_broadcast_pin_config_radio_group" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <include + android:id="@+id/bluetooth_broadcast_pin_unencrypted" + layout="@layout/preference_widget_dialog_radiobutton"/> + + <include + android:id="@+id/bluetooth_broadcast_pin_4" + layout="@layout/preference_widget_dialog_radiobutton"/> + + <include + android:id="@+id/bluetooth_broadcast_pin_16" + layout="@layout/preference_widget_dialog_radiobutton"/> + + </RadioGroup> + + </LinearLayout> + +</ScrollView> diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index 9df195584d..7ee1524010 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -61,4 +61,20 @@ settings:searchable="false" settings:controller="com.android.settings.bluetooth.BluetoothDetailsMacAddressController"/> -</PreferenceScreen>
\ No newline at end of file + <com.android.settingslib.widget.ActionButtonsPreference + android:key="sync_helper_buttons" + settings:allowDividerBelow="true"/> + <PreferenceCategory + android:key="added_sources" + android:title="@string/added_sources_dashboard_title"> + </PreferenceCategory> + + <com.android.settings.bluetooth.BADeviceVolumePreference + android:key="ba_device_volume" + android:icon="@drawable/ic_volume_media_bt" + android:title="@string/bcast_sink_volume_title" + settings:allowDividerBelow="true" + settings:allowDividerAbove="true" + settings:controller="com.android.settings.bluetooth.BADeviceVolumeController"/> + +</PreferenceScreen> diff --git a/res/xml/bluetooth_group_details_fragment.xml b/res/xml/bluetooth_group_details_fragment.xml new file mode 100644 index 0000000000..47e7dc442d --- /dev/null +++ b/res/xml/bluetooth_group_details_fragment.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +--> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:title="@string/group_options"> + <com.android.settings.widget.GroupOptionsPreference + android:key="group_options" /> + <PreferenceCategory + android:key="group_options_active_devices" + android:title="@string/group_active_devices" /> + <PreferenceCategory + android:key="group_options_connected_devices" + android:title="@string/group_connected_devices" /> + <PreferenceCategory + android:key="group_options_bonded_devices" + android:title="@string/group_bonded_devices" /> +</PreferenceScreen>
\ No newline at end of file diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml index 86dc8a12c7..14f845fc22 100644 --- a/res/xml/bluetooth_screen.xml +++ b/res/xml/bluetooth_screen.xml @@ -24,6 +24,16 @@ android:summary="@string/summary_placeholder" settings:controller="com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController"/> + <com.android.settingslib.RestrictedSwitchPreference + android:key="bluetooth_screen_broadcast_enable" + android:title="@string/bluetooth_broadcast" + android:summary="@string/summary_placeholder"/> + + <com.android.settingslib.RestrictedPreference + android:key="bluetooth_screen_broadcast_pin_configure" + android:title="@string/bluetooth_broadcast_pin_configure" + android:summary="@string/summary_placeholder"/> + <com.android.settingslib.RestrictedPreference android:key="bluetooth_screen_add_bt_devices" android:title="@string/bluetooth_pairing_pref_title" diff --git a/res/xml/bluetooth_search_bcast_sources.xml b/res/xml/bluetooth_search_bcast_sources.xml new file mode 100644 index 0000000000..482af5a40e --- /dev/null +++ b/res/xml/bluetooth_search_bcast_sources.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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="bluetooth_search_bcast_sources_detail_screen" + android:title="@string/bluetooth_search_bcast_sources_pref_title"> + + <Preference + android:key="bt_bcast_rcvr_device" + android:title="@string/bluetooth_bcast_rcvr_device_name" + android:summary="@string/summary_placeholder" + /> + + <com.android.settings.bluetooth.BluetoothProgressCategory + android:key="available_audio_sources" + android:title="@string/bluetooth_paired_device_title"/> + + <com.android.settingslib.widget.FooterPreference/> +</PreferenceScreen> + diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index 8e606484b7..6291547e22 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -36,6 +36,11 @@ android:title="@string/connected_device_other_device_title" settings:controller="com.android.settings.connecteddevice.ConnectedDeviceGroupController"/> + <PreferenceCategory + android:key="saved_tws_device_list" + android:title="@string/connected_tws_device_saved_title" + settings:controller="com.android.settings.connecteddevice.SavedTwsDeviceGroupController"/> + <com.android.settingslib.RestrictedPreference android:key="add_bt_devices" android:title="@string/bluetooth_pairing_pref_title" @@ -48,6 +53,71 @@ settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/> <PreferenceCategory + android:key="group_connected_device_list" + settings:controller="com.android.settings.connecteddevice.GroupConnectedBluetoothDevicesController" + android:layout="@layout/preference_category_no_label"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_one" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_two" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_three" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_four" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_five" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_six" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_seven" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_eight" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_nine" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="group_remaining" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <PreferenceCategory android:key="previously_connected_devices" android:title="@string/connected_device_previously_connected_title" settings:controller="com.android.settings.connecteddevice.PreviouslyConnectedDevicePreferenceController"> @@ -61,6 +131,20 @@ android:fragment="com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment"/> </PreferenceCategory> + <PreferenceCategory + android:key="group_previously_connected_devices" + android:title="@string/group_previously_connected_screen_title" + settings:controller="com.android.settings.connecteddevice.GroupPreviouslyConnectedDevicePreferenceController"> + + <Preference + android:key="group_previously_connected_devices_see_all" + android:title="@string/previous_connected_see_all" + android:icon="@drawable/ic_chevron_right_24dp" + android:order="100" + settings:searchable="false" + android:fragment="com.android.settings.connecteddevice.GroupPreviouslyConnectedDeviceDashboardFragment"/> + </PreferenceCategory> + <Preference android:key="connection_preferences" android:title="@string/connected_device_connections_title" diff --git a/res/xml/developer_mobile_network_list.xml b/res/xml/developer_mobile_network_list.xml new file mode 100644 index 0000000000..c966f73e40 --- /dev/null +++ b/res/xml/developer_mobile_network_list.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) 2020, The Linux Foundation. All rights reserved + Not a contribution + + 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. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:title="@string/network_settings_title"> + + <com.android.settingslib.RestrictedPreference + android:key="prefer_5g_add_more" + settings:isPreferenceVisible="false" + settings:userRestriction="no_config_mobile_networks" + settings:useAdminDisabledSummary="true" + settings:searchable="false" + android:title="@string/mobile_network_list_add_more" + android:icon="@drawable/ic_menu_add_activated_tint" + android:order="100" > + <intent android:action="android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"> + <extra android:name="android.telephony.euicc.extra.FORCE_PROVISION" + android:value="true"/> + </intent> + </com.android.settingslib.RestrictedPreference> + +</PreferenceScreen> diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index e389747bf6..34ce000c53 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -263,12 +263,28 @@ android:title="@string/debug_networking_category" android:order="400"> + <com.android.settings.widget.AddPreference + android:key="prefer_5G_nr_mode" + android:title="@string/prefer_5G_nr_mode_title" + settings:controller="com.android.settings.development.Prefer5GNetworkSummaryController"/> + + <com.android.settingslib.RestrictedPreference + android:fragment="com.android.settings.development.PreferVonrSettings" + android:key="prefer_vonr_mode" + android:title="@string/prefer_vonr_mode_title" + android:summary="@string/prefer_vonr_mode_summary"/> + <SwitchPreference android:key="wifi_display_certification" android:title="@string/wifi_display_certification" android:summary="@string/wifi_display_certification_summary" /> <SwitchPreference + android:key="wifi_coverage_extend" + android:title="@string/wifi_coverage_extend" + android:summary="@string/wifi_coverage_extend_summary" /> + + <SwitchPreference android:key="wifi_verbose_logging" android:title="@string/wifi_verbose_logging" android:summary="@string/wifi_verbose_logging_summary" /> @@ -289,6 +305,12 @@ android:summary="@string/mobile_data_always_on_summary" /> <SwitchPreference + android:key="smart_dds_switch" + android:title="@string/smart_dds_switch" + android:summary="@string/smart_dds_switch_summary" + android:enabled="false" /> + + <SwitchPreference android:key="tethering_hardware_offload" android:title="@string/tethering_hardware_offload" android:summary="@string/tethering_hardware_offload_summary" /> diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index fe87efd73d..15128dd913 100644..100755 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -35,6 +35,13 @@ settings:controller="com.android.settings.location.RecentLocationAccessSeeAllButtonPreferenceController" settings:searchable="false"/> + <CheckBoxPreference + android:key="assisted_gps" + android:title="@string/assisted_gps" + android:summaryOn="@string/assisted_gps_enabled" + android:summaryOff="@string/assisted_gps_disabled" + settings:controller="com.android.settings.location.AgpsPreferenceController"/> + <PreferenceCategory android:key="location_advanced_settings" android:layout="@layout/preference_category_no_label"> diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 673994a9a9..8e8b350b69 100644..100755 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -34,10 +34,15 @@ android:selectable="false"/> <ListPreference + android:key="data_preference" + android:title="@string/data_preference" + settings:controller="com.android.settings.network.telephony.DataDefaultSubscriptionController" + settings:allowDividerAbove="true"/> + + <ListPreference android:key="calls_preference" android:title="@string/calls_preference" - settings:controller="com.android.settings.network.telephony.CallsDefaultSubscriptionController" - settings:allowDividerAbove="true"/> + settings:controller="com.android.settings.network.telephony.CallsDefaultSubscriptionController"/> <ListPreference android:key="sms_preference" @@ -116,7 +121,6 @@ android:summary="@string/enhanced_4g_lte_mode_summary_4g_calling" settings:keywords="@string/keywords_enhance_4g_lte" settings:controller="com.android.settings.network.telephony.Enhanced4gCallingPreferenceController"/> - <SwitchPreference android:key="advance_call" android:title="@string/enhanced_4g_lte_mode_title_advanced_calling" @@ -124,6 +128,11 @@ android:summary="@string/enhanced_4g_lte_mode_summary" settings:keywords="@string/keywords_enhance_4g_lte" settings:controller="com.android.settings.network.telephony.Enhanced4gAdvancedCallingPreferenceController"/> + <SwitchPreference + android:key="enabled_5g" + android:title="@string/enabled_5g_mode_title" + android:persistent="true" + settings:controller="com.android.settings.network.telephony.Enabled5GPreferenceController"/> <SwitchPreference android:key="contact_discovery_opt_in" @@ -235,6 +244,15 @@ settings:controller="com.android.settings.network.telephony.ApnPreferenceController"/> <Preference + android:key="uplmn_settings_key" + android:persistent="false" + android:title="@string/uplmn_settings_title" + android:summary="@string/uplmn_settings_summary" + settings:allowDividerAbove="true" + settings:controller="com.android.settings.network.telephony.UserPLMNPreferenceController"> + </Preference> + + <Preference android:key="carrier_settings_key" android:title="@string/carrier_settings_title" settings:controller="com.android.settings.network.telephony.CarrierPreferenceController"> diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml index 9547318a86..5c18af0510 100644 --- a/res/xml/my_device_info.xml +++ b/res/xml/my_device_info.xml @@ -211,4 +211,22 @@ settings:controller="com.android.settings.deviceinfo.BuildNumberPreferenceController"/> </PreferenceCategory> + <!-- Software version --> + <Preference + android:key="software_version" + android:order="44" + android:title="@string/software_version" + android:summary="@string/summary_placeholder" + android:selectable="false" + settings:enableCopying="true"/> + + <!-- Storage size, including two items: ROM and RAM total size --> + <Preference + android:key="key_storage_total_size" + android:order="45" + android:title="@string/ram_total_size" + android:summary="@string/summary_placeholder" + android:selectable="false" + settings:allowDividerAbove="true" + settings:enableCopying="true"/> </PreferenceScreen> diff --git a/res/xml/other_sound_settings.xml b/res/xml/other_sound_settings.xml index 7b3f362eec..d0ddc2257e 100644 --- a/res/xml/other_sound_settings.xml +++ b/res/xml/other_sound_settings.xml @@ -24,6 +24,11 @@ android:key="dial_pad_tones" android:title="@string/dial_pad_tones_title" /> + <!-- Call connected tones --> + <SwitchPreference + android:key="call_connected_tones" + android:title="@string/call_connected_tones_title" /> + <!-- Screen locking sounds --> <SwitchPreference android:key="screen_locking_sounds" diff --git a/res/xml/prefer_vonr_list.xml b/res/xml/prefer_vonr_list.xml new file mode 100644 index 0000000000..7fab44784c --- /dev/null +++ b/res/xml/prefer_vonr_list.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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" + android:title="@string/prefer_vonr_mode_title"> +</PreferenceScreen> diff --git a/res/xml/previously_connected_group_devices.xml b/res/xml/previously_connected_group_devices.xml new file mode 100644 index 0000000000..c45d9ff6e1 --- /dev/null +++ b/res/xml/previously_connected_group_devices.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:title="@string/group_previously_connected_screen_title"> + <PreferenceCategory + android:key="group_saved_device_list" + settings:controller="com.android.settings.connecteddevice.GroupSavedDeviceController"/> + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_one" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_two" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_three" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_four" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_five" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_six" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_seven" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_eight" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_nine" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> + + <com.android.settings.widget.GroupPreferenceCategory + android:key="pcg_remaining" + android:layout="@layout/preference_category_no_label" + settings:allowDividerAbove="true" + settings:allowDividerBelow="true"/> +</PreferenceScreen> diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index e30f13915b..1010a294e9 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -218,6 +218,12 @@ android:title="@string/emergency_tone_title" android:summary="%s" android:order="-10"/> + <!-- Call connected tones --> + + <SwitchPreference + android:key="call_connected_tones" + android:title="@string/call_connected_tones_title" /> + <Preference android:key="sound_work_settings" diff --git a/res/xml/uplmn_editor.xml b/res/xml/uplmn_editor.xml new file mode 100755 index 0000000000..30199d15db --- /dev/null +++ b/res/xml/uplmn_editor.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2020, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" + android:title="@string/uplmn_settings_title"> + <Preference + android:title="@string/network_id" + android:key="network_id_key" /> + <EditTextPreference + android:title="@string/dialog_network_priority_title" + android:dialogTitle="@string/dialog_network_priority_title" + android:key="priority_key" + android:singleLine="true" + android:inputType="number" /> + <ListPreference + android:title="@string/gsm_umts_network_preferences_title" + android:dialogTitle="@string/gsm_umts_network_preferences_dialogtitle" + android:key="network_mode_key" + android:entries="@array/uplmn_prefer_network_mode_td_choices" + android:entryValues="@array/uplmn_prefer_network_mode_values" /> +</PreferenceScreen> diff --git a/res/xml/uplmn_settings.xml b/res/xml/uplmn_settings.xml new file mode 100755 index 0000000000..dfca65547c --- /dev/null +++ b/res/xml/uplmn_settings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2020, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:title="@string/uplmn_settings_title" + android:key="button_uplmn_list_key" + android:persistent="false"> +</PreferenceScreen> diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index 21f347bb02..34d8032cbb 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -37,12 +37,12 @@ android:persistent="false" android:title="@string/wifi_hotspot_password_title"/> + <ListPreference + android:key="wifi_tether_network_ap_band" + android:title="@string/wifi_hotspot_ap_band_title"/> + <SwitchPreference android:key="wifi_tether_auto_turn_off" android:title="@string/wifi_hotspot_auto_off_title" android:summary="@string/wifi_hotspot_auto_off_summary"/> - - <SwitchPreference - android:key="wifi_tether_maximize_compatibility" - android:title="@string/wifi_hotspot_maximize_compatibility"/> </PreferenceScreen> diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java index 735ae23e78..738c960379 100644 --- a/src/com/android/settings/AirplaneModeEnabler.java +++ b/src/com/android/settings/AirplaneModeEnabler.java @@ -20,6 +20,7 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; import android.os.Looper; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.telephony.PhoneStateListener; @@ -140,6 +141,10 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { } } + public boolean isInScbm() { + return SystemProperties.getBoolean("ril.inscbm", false); + } + /** * Check the status of ECM mode * @@ -167,9 +172,9 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { } public void setAirplaneMode(boolean isAirplaneModeOn) { - if (isInEcmMode()) { - // In ECM mode, do not update database at this point - Log.d(LOG_TAG, "ECM airplane mode=" + isAirplaneModeOn); + if (isInEcmMode() || isInScbm()) { + // In Emergency mode, do not update database at this point + Log.d(LOG_TAG, "Emergency mode airplane mode=" + isAirplaneModeOn); } else { mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AIRPLANE_TOGGLE, isAirplaneModeOn); @@ -177,9 +182,11 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { } } - public void setAirplaneModeInECM(boolean isECMExit, boolean isAirplaneModeOn) { - Log.d(LOG_TAG, "Exist ECM=" + isECMExit + ", with airplane mode=" + isAirplaneModeOn); - if (isECMExit) { + public void setAirplaneModeInEmergencyMode(boolean isEmergencyModeExit, + boolean isAirplaneModeOn) { + Log.d(LOG_TAG, "Exist Emergency Mode=" + isEmergencyModeExit + + ", with airplane mode=" + isAirplaneModeOn); + if (isEmergencyModeExit) { // update database based on the current checkbox state setAirplaneModeOn(isAirplaneModeOn); } else { diff --git a/src/com/android/settings/DBReadAsyncTask.java b/src/com/android/settings/DBReadAsyncTask.java new file mode 100644 index 0000000000..6cb5429f8e --- /dev/null +++ b/src/com/android/settings/DBReadAsyncTask.java @@ -0,0 +1,108 @@ +/* +Copyright (c) 2016, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; + +public class DBReadAsyncTask extends AsyncTask<Void, Void, Boolean> { + + /** + * SMQ preferences key. + */ + public static final String SMQ_KEY_VALUE = "app_status"; + + /** + * The authority of the provider. + */ + public static final String AUTHORITY = "com.qti.smq.Feedback.provider"; + /** + * The content URI. + */ + final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + /** + * The Content URI for this table. + */ + final Uri SNAP_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "smq_settings"); + + Context mContext; + + public static final String KEY_VALUE = "app_status"; + + public DBReadAsyncTask(Context mContext) { + super(); + this.mContext = mContext; + } + + @Override + protected Boolean doInBackground(final Void... params) { + final String whereClause = "key" + "=?"; + final String[] selectionArgs = { KEY_VALUE }; + + final Cursor c = mContext.getContentResolver().query( + SNAP_CONTENT_URI, null, whereClause, + selectionArgs, null); + final SharedPreferences sharedPreferences = mContext + .getSharedPreferences(SmqSettings.SMQ_PREFS_NAME, Context.MODE_PRIVATE); + + if (c!= null && c.getCount() > 0) { + c.moveToFirst(); + final int value = c.getInt(1); + + final int appStatus = sharedPreferences.getInt(KEY_VALUE, 0); + if (appStatus == value) { + // Do nothing + } else { + //Save preference and notify. + final SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(KEY_VALUE, value); + editor.commit(); + + } + + } + else{ + //No such table. don't show menu. + final SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(KEY_VALUE, 0); + editor.commit(); + } + if(c != null){ + c.close(); + } + + return true; + } + +}
\ No newline at end of file diff --git a/src/com/android/settings/IccLockSettings.java b/src/com/android/settings/IccLockSettings.java index e89e84b9df..2b8dcba38c 100644 --- a/src/com/android/settings/IccLockSettings.java +++ b/src/com/android/settings/IccLockSettings.java @@ -133,6 +133,7 @@ public class IccLockSettings extends SettingsPreferenceFragment private int mSlotId = -1; private int mSubId; private TelephonyManager mTelephonyManager; + private SubscriptionManager mSubscriptionManager; // For replies from IccCard interface private Handler mHandler = new Handler() { @@ -185,6 +186,7 @@ public class IccLockSettings extends SettingsPreferenceFragment mProxySubscriptionMgr.setLifecycle(getLifecycle()); mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); addPreferencesFromResource(R.xml.sim_lock_settings); @@ -276,7 +278,11 @@ public class IccLockSettings extends SettingsPreferenceFragment for (int i = 0; i < numSims; ++i) { final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(i); if (subInfo != null) { - componenterList.add(subInfo); + final int slot = subInfo.getSimSlotIndex(); + if (mSubscriptionManager.getSimStateForSlotIndex(slot) != + TelephonyManager.SIM_STATE_NOT_READY) { + componenterList.add(subInfo); + } } } @@ -334,6 +340,11 @@ public class IccLockSettings extends SettingsPreferenceFragment final SubscriptionInfo sir = getVisibleSubscriptionInfoForSimSlotIndex(mSlotId); final int subId = (sir != null) ? sir.getSubscriptionId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID; + mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); + + int cardState = mTelephonyManager.getSimState(); + boolean canInteract = cardState == TelephonyManager.SIM_STATE_READY || + cardState == TelephonyManager.SIM_STATE_LOADED; if (mSubId != subId) { mSubId = subId; @@ -344,10 +355,10 @@ public class IccLockSettings extends SettingsPreferenceFragment } if (mPinDialog != null) { - mPinDialog.setEnabled(sir != null); + mPinDialog.setEnabled(sir != null && canInteract); } if (mPinToggle != null) { - mPinToggle.setEnabled(sir != null); + mPinToggle.setEnabled(sir != null && canInteract); if (sir != null) { mPinToggle.setChecked(isIccLockEnabled()); diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java index f79bdb2e36..14eabf66fa 100644 --- a/src/com/android/settings/ResetNetworkConfirm.java +++ b/src/com/android/settings/ResetNetworkConfirm.java @@ -19,6 +19,7 @@ package com.android.settings; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.Activity; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; @@ -49,12 +50,13 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; import com.android.settings.core.InstrumentedFragment; import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper; import com.android.settings.network.apn.ApnSettings; import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothManager; /** * Confirm and execute a reset of the network settings to a clean "just out of the box" @@ -139,6 +141,13 @@ public class ResetNetworkConfirm extends InstrumentedFragment { BluetoothAdapter btAdapter = btManager.getAdapter(); if (btAdapter != null) { btAdapter.factoryReset(); + LocalBluetoothManager mLocalBtManager = + LocalBluetoothManager.getInstance(mContext, null); + if (mLocalBtManager != null) { + CachedBluetoothDeviceManager cachedDeviceManager = + mLocalBtManager.getCachedDeviceManager(); + cachedDeviceManager.clearAllDevices(); + } } } diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 7154081841..cc8d87dcb1 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -314,6 +314,8 @@ public class Settings extends SettingsActivity { public static class StorageDashboardActivity extends SettingsActivity {} public static class AccountDashboardActivity extends SettingsActivity {} public static class SystemDashboardActivity extends SettingsActivity {} + public static class SupportDashboardActivity extends SettingsActivity {} + public static class SMQQtiFeedbackActivity extends SettingsActivity { /* empty */ } /** * Activity for MediaControlsSettings diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index d3d3604a2c..7941562872 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -80,7 +80,6 @@ import com.google.android.setupcompat.util.WizardManagerHelper; import java.util.ArrayList; import java.util.List; - public class SettingsActivity extends SettingsBaseActivity implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, @@ -171,6 +170,7 @@ public class SettingsActivity extends SettingsBaseActivity private CharSequence mInitialTitle; private int mInitialTitleResId; + private SmqSettings mSMQ; private BroadcastReceiver mDevelopmentSettingsListener; @@ -270,6 +270,8 @@ public class SettingsActivity extends SettingsBaseActivity getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0)); } + mSMQ = new SmqSettings(getApplicationContext()); + // Getting Intent properties can only be done after the super.onCreate(...) final String initialFragmentName = getInitialFragmentName(intent); @@ -691,7 +693,16 @@ public class SettingsActivity extends SettingsBaseActivity */ private void switchToFragment(String fragmentName, Bundle args, boolean validate, int titleResId, CharSequence title) { + if (fragmentName.equals(getString(R.string.qtifeedback_intent_action))){ + final Intent newIntent = new Intent(getString(R.string.qtifeedback_intent_action)); + newIntent.addCategory("android.intent.category.DEFAULT"); + startActivity(newIntent); + finish(); + return; + } + Log.d(LOG_TAG, "Switching to fragment " + fragmentName); + if (validate && !isValidFragment(fragmentName)) { throw new IllegalArgumentException("Invalid fragment for this activity: " + fragmentName); @@ -735,6 +746,10 @@ public class SettingsActivity extends SettingsBaseActivity pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin) || somethingChanged; + if(mSMQ.isShowSmqSettings()){ + somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, Settings.SMQQtiFeedbackActivity.class.getName()), mSMQ.isShowSmqSettings(), isAdmin) || somethingChanged; + } + // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise // enable DataPlanUsageSummaryActivity. somethingChanged = setTileEnabled(changedList, @@ -749,7 +764,7 @@ public class SettingsActivity extends SettingsBaseActivity isAdmin) || somethingChanged; somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, - Settings.PowerUsageSummaryActivity.class.getName()), + Settings.PowerUsageSummaryActivity.class.getName()), mBatteryPresent, isAdmin) || somethingChanged; somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, diff --git a/src/com/android/settings/SmqSettings.java b/src/com/android/settings/SmqSettings.java new file mode 100644 index 0000000000..65f87039fb --- /dev/null +++ b/src/com/android/settings/SmqSettings.java @@ -0,0 +1,65 @@ +/* +Copyright (c) 2017, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.settings; + +import android.content.Context; +import android.content.SharedPreferences; + +public class SmqSettings { + + /** + * The application context. + */ + private Context mContext; + private SharedPreferences mSmqPreferences; + + /** + * SMQ preferences key. + */ + public static final String SMQ_KEY_VALUE = "app_status"; + + /** + * Shared preferences name. + */ + public static final String SMQ_PREFS_NAME = "smqpreferences"; + + public SmqSettings(final Context context) { + mContext = context; + new DBReadAsyncTask(mContext).execute(); + mSmqPreferences = mContext.getSharedPreferences( + SMQ_PREFS_NAME, Context.MODE_PRIVATE); + } + + public boolean isShowSmqSettings() { + final int iShowSmq = mSmqPreferences.getInt(SMQ_KEY_VALUE, 0); + final boolean showSmq = iShowSmq > 0 ? true : false; + return showSmq; + } + +} diff --git a/src/com/android/settings/TestingSettingsBroadcastReceiver.java b/src/com/android/settings/TestingSettingsBroadcastReceiver.java index 30a0d796c1..3f7bce5447 100644 --- a/src/com/android/settings/TestingSettingsBroadcastReceiver.java +++ b/src/com/android/settings/TestingSettingsBroadcastReceiver.java @@ -35,7 +35,7 @@ public class TestingSettingsBroadcastReceiver extends BroadcastReceiver { && intent.getAction().equals(TelephonyManager.ACTION_SECRET_CODE)) { Intent i = new Intent(Intent.ACTION_MAIN); i.setClass(context, TestingSettingsActivity.class); - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(i); } } diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 1a21b5582a..16b68ccfdc 100644..100755 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -162,7 +162,7 @@ public class TetherSettings extends RestrictedSettingsFragment final Activity activity = getActivity(); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, BluetoothProfile.PAN); } @@ -285,12 +285,20 @@ public class TetherSettings extends RestrictedSettingsFragment updateBluetoothAndEthernetState(); updateUsbPreference(); } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + int status = intent + .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); if (mBluetoothEnableForTether) { - switch (intent - .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { + switch (status) { case BluetoothAdapter.STATE_ON: startTethering(TETHERING_BLUETOOTH); mBluetoothEnableForTether = false; + if (mBluetoothPan.get() == null) { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(getActivity().getApplicationContext(), + mProfileServiceListener, BluetoothProfile.PAN); + } + } break; case BluetoothAdapter.STATE_OFF: @@ -301,6 +309,18 @@ public class TetherSettings extends RestrictedSettingsFragment default: // ignore transition states } + } else { + switch (status) { + case BluetoothAdapter.STATE_ON: + if (mBluetoothPan.get() == null) { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(getActivity().getApplicationContext(), + mProfileServiceListener, BluetoothProfile.PAN); + } + } + break; + } } updateBluetoothAndEthernetState(); } else if (action.equals(BluetoothPan.ACTION_TETHERING_STATE_CHANGED)) { diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 50f6003ce5..d2f7c21245 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -45,6 +45,7 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.Resources.NotFoundException; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -70,6 +71,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; +import android.os.SystemProperties; import android.os.storage.VolumeInfo; import android.preference.PreferenceFrameLayout; import android.provider.ContactsContract.CommonDataKinds; @@ -77,6 +79,9 @@ import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; +import android.provider.Settings; +import android.provider.Telephony; +import android.service.persistentdata.PersistentDataBlockManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.Spannable; @@ -119,6 +124,8 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; +import org.codeaurora.internal.IExtTelephony; + public final class Utils extends com.android.settingslib.Utils { private static final String TAG = "Settings"; @@ -134,6 +141,14 @@ public final class Utils extends com.android.settingslib.Utils { public static final String OS_PKG = "os"; + public static final String KEY_SOFTWARE_VERSION = "ext_meta_software_version"; + public static final String KEY_MODEL = "ext_model_name_from_meta"; + public static final String KEY_HARDWARE_VERSION = "ext_hardware_version"; + public static final String KEY_WIFI_MAC_ADDRESS = "ext_wifi_mac_address"; + public static final String KEY_DEVICE_NAME = "ext_device_name"; + public static final String KEY_ROM_TOTAL_SIZE = "ext_rom_total_size"; + public static final String KEY_RAM_TOTAL_SIZE = "ext_ram_total_size"; + /** * Whether to disable the new device identifier access restrictions. */ @@ -1103,6 +1118,53 @@ public final class Utils extends com.android.settingslib.Utils { return false; } + public static String getLocalizedName(Context context, String resName) { + if(context == null){ + return null; + } + // If can find a localized name, replace the APN name with it + String localizedName = null; + if (resName != null && !resName.isEmpty()) { + int resId = context.getResources().getIdentifier(resName, "string", + context.getPackageName()); + if(resId > 0){ + try { + localizedName = context.getResources().getString(resId); + Log.d(TAG, "Replaced apn name with localized name"); + } catch (NotFoundException e) { + Log.e(TAG, "Got execption while getting the localized apn name.", e); + } + } + } + return localizedName; + } + + public static boolean carrierTableFieldValidate(String field){ + if(field == null) + return false; + if(Telephony.Carriers.AUTH_TYPE.equalsIgnoreCase(field) + || Telephony.Carriers.SUBSCRIPTION_ID.equalsIgnoreCase(field)) + return true; + field = field.toUpperCase(); + Class clazz = Telephony.Carriers.class; + try{ + clazz.getDeclaredField(field); + }catch(NoSuchFieldException e){ + Log.w(TAG, field + "is not a valid field in class Telephony.Carriers"); + return false; + } + return true; + } + + public static boolean isSupportCTPA(Context context) { + Context appContext = context.getApplicationContext(); + return appContext.getResources().getBoolean(R.bool.config_support_CT_PA); + } + + public static String getString(Context context, String key) { + return Settings.Global.getString(context.getContentResolver(), key); + } + /** Get {@link Resources} by subscription id if subscription id is valid. */ public static Resources getResourcesForSubId(Context context, int subId) { if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { diff --git a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java index 670b5a8ffd..4147a7022b 100644 --- a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java +++ b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java @@ -21,6 +21,8 @@ import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.INotificationManager; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java index 27d63bfdb7..18057a5631 100644 --- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java @@ -93,6 +93,19 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater Log.d(TAG, "isFilterMatched() device : " + cachedDevice.getName() + ", isFilterMatched : " + isFilterMatched); } + if (isFilterMatched) { + if (isGroupDevice(cachedDevice)) { + isFilterMatched = false; + if (DBG) { + Log.d(TAG, "It is GroupDevice ignore showing "); + } + } else if (isPrivateAddr(cachedDevice)) { + isFilterMatched = false; + if (DBG) { + Log.d(TAG, "It is isPrivateAddr ignore showing "); + } + } + } } return isFilterMatched; } diff --git a/src/com/android/settings/bluetooth/BADeviceVolumeController.java b/src/com/android/settings/bluetooth/BADeviceVolumeController.java new file mode 100644 index 0000000000..9267a74ca8 --- /dev/null +++ b/src/com/android/settings/bluetooth/BADeviceVolumeController.java @@ -0,0 +1,277 @@ +/* + *Copyright (c) 2020, The Linux Foundation. All rights reserved. + *Not a contribution + */ + +/* + * Copyright (C) 2016 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.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothVcp; +import android.content.Context; +import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.SliderPreferenceController; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.bluetooth.VcpProfile; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.bluetooth.HeadsetProfile; +import android.bluetooth.BluetoothHeadset; +import java.lang.Class; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Class for preference controller that handles BADeviceVolumePreference + */ +public class BADeviceVolumeController extends + SliderPreferenceController implements CachedBluetoothDevice.Callback, + LifecycleObserver, OnPause, OnResume { + + private static final String TAG = "BADeviceVolumeController"; + public static final int BROADCAST_AUDIO_MASK = 0x02; + public static final String BLUETOOTH_ADV_AUDIO_MASK_PROP = + "persist.vendor.service.bt.adv_audio_mask"; + public static final String BLUETOOTH_VCP_FOR_BROADCAST_PROP = + "persist.vendor.service.bt.vcpForBroadcast"; + private static final String KEY_BA_DEVICE_VOLUME = "ba_device_volume"; + private static final String VCACHED_DEVICE_CLASS = + "com.android.settingslib.bluetooth.VendorCachedBluetoothDevice"; + + protected BADeviceVolumePreference mPreference; + private CachedBluetoothDevice mCachedDevice; + protected LocalBluetoothProfileManager mProfileManager; + private LocalBluetoothManager mLocalBluetoothManager; + private VcpProfile mVcpProfile = null; + private boolean mIsVcpForBroadcastSupported = false; + private HeadsetProfile mHeadsetProfile; + private Class<?> mVCachedDeviceClass = null; + private Object mVendorCachedDevice = null; + @VisibleForTesting + AudioManager mAudioManager; + + public BADeviceVolumeController(Context context) { + super(context, KEY_BA_DEVICE_VOLUME); + int advAudioMask = SystemProperties.getInt(BLUETOOTH_ADV_AUDIO_MASK_PROP, 0); + mIsVcpForBroadcastSupported = + (((advAudioMask & BROADCAST_AUDIO_MASK) == BROADCAST_AUDIO_MASK) && + SystemProperties.getBoolean(BLUETOOTH_VCP_FOR_BROADCAST_PROP, false)); + Log.d(TAG, "mIsVcpForBroadcastSupported: " + mIsVcpForBroadcastSupported); + } + + @Override + public int getAvailabilityStatus() { + Log.d(TAG, "getAvailabilityStatus"); + if(mIsVcpForBroadcastSupported) { + return AVAILABLE; + } else { + return UNSUPPORTED_ON_DEVICE; + } + } + + @Override + public boolean isSliceable() { + return TextUtils.equals(getPreferenceKey(), KEY_BA_DEVICE_VOLUME); + } + + @Override + public boolean isPublicSlice() { + return true; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + if (isAvailable()) { + mPreference = screen.findPreference(getPreferenceKey()); + if (mAudioManager != null) { + mPreference.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)); + mPreference.setMin(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)); + refresh(); + } else { + mPreference.setVisible(false); + } + } + } + + @Override + public void onPause() { + Log.d(TAG, "onPause"); + if (mCachedDevice != null) { + mCachedDevice.unregisterCallback(this); + } + } + + @Override + public void onResume() { + Log.d(TAG, "onResume"); + if (mCachedDevice != null) { + mCachedDevice.registerCallback(this); + refresh(); + } + } + + public void init(DashboardFragment fragment, LocalBluetoothManager manager, + CachedBluetoothDevice device) { + Log.d(TAG, "Init"); + if (mIsVcpForBroadcastSupported) { + mCachedDevice = device; + mLocalBluetoothManager = manager; + mProfileManager = mLocalBluetoothManager.getProfileManager(); + mVcpProfile = mProfileManager.getVcpProfile(); + mAudioManager = mContext.getSystemService(AudioManager.class); + + try { + mVCachedDeviceClass = Class.forName(VCACHED_DEVICE_CLASS); + Class[] arg = new Class[2]; + arg[0] = CachedBluetoothDevice.class; + arg[1] = LocalBluetoothProfileManager.class; + Method getVendorCachedBluetoothDevice = mVCachedDeviceClass.getDeclaredMethod( + "getVendorCachedBluetoothDevice", arg); + mVendorCachedDevice = (Object)getVendorCachedBluetoothDevice.invoke( + null, mCachedDevice, mProfileManager); + } catch (ClassNotFoundException | NoSuchMethodException + | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + mHeadsetProfile = mProfileManager.getHeadsetProfile(); + } + } + + protected void refresh() { + Log.d(TAG, "refresh"); + if (!mIsVcpForBroadcastSupported || mVcpProfile == null) { + Log.d(TAG, "VCP for broadcast is not supported"); + return; + } + boolean showSlider = enableSlider(); + BluetoothDevice device = mCachedDevice.getDevice(); + int audioState = mHeadsetProfile.getAudioState(device); + boolean inCall = (audioState == BluetoothHeadset.STATE_AUDIO_CONNECTING || + audioState == BluetoothHeadset.STATE_AUDIO_CONNECTED); + Log.d(TAG,"VCP refresh showSlider: " + showSlider + " inCall: " + inCall); + if ((mVcpProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED) && + ((mVcpProfile.getConnectionMode(device) & BluetoothVcp.MODE_BROADCAST) != 0)) { + Log.d(TAG, "VCP is connected for broadcast "); + mPreference.setVisible(true); + if (!showSlider || inCall) { + mPreference.setProgress(0); + mPreference.setEnabled(false); + return; + } + mPreference.setEnabled(true); + int position = mVcpProfile.getAbsoluteVolume(device); + + if (position != -1) { + mPreference.setProgress(position); + } + } else { + mPreference.setVisible(false); + } + } + + @Override + public void onDeviceAttributesChanged() { + refresh(); + } + + @Override + public String getPreferenceKey() { + return KEY_BA_DEVICE_VOLUME; + } + + @Override + public int getSliderPosition() { + if (mPreference != null) { + return mPreference.getProgress(); + } + if (mVcpProfile != null) { + return mVcpProfile.getAbsoluteVolume(mCachedDevice.getDevice()); + } + return 0; + } + + @Override + public boolean setSliderPosition(int position) { + if (mPreference != null) { + mPreference.setProgress(position); + } + if (mVcpProfile != null) { + mVcpProfile.setAbsoluteVolume(mCachedDevice.getDevice(), position); + return true; + } + return false; + } + + @Override + public int getMax() { + if (mPreference != null) { + return mPreference.getMax(); + } + if (mAudioManager != null) { + return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + } + return 0; + } + + @Override + public int getMin() { + if (mPreference != null) { + return mPreference.getMin(); + } + if (mAudioManager != null) { + return mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); + } + return 0; + } + private boolean enableSlider() { + if (mVCachedDeviceClass == null || mVendorCachedDevice == null) { + Log.d(TAG,"enableSlider: false"); + return false; + } + + try { + Method isBroadcastAudioSynced = + mVCachedDeviceClass.getDeclaredMethod("isBroadcastAudioSynced"); + Boolean ret = (Boolean)isBroadcastAudioSynced.invoke(mVendorCachedDevice); + Log.d(TAG,"enableSlider: " + ret); + return ret; + } catch(IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + Log.i(TAG, "Exception" + e); + } + + Log.d(TAG,"enableSlider: false"); + return false; + } +} + diff --git a/src/com/android/settings/bluetooth/BADeviceVolumePreference.java b/src/com/android/settings/bluetooth/BADeviceVolumePreference.java new file mode 100644 index 0000000000..add5f1b5be --- /dev/null +++ b/src/com/android/settings/bluetooth/BADeviceVolumePreference.java @@ -0,0 +1,67 @@ +/* + *Copyright (c) 2020, The Linux Foundation. All rights reserved. + *Not a contribution + */ + +/* + * Copyright (C) 2014 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.bluetooth; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; +import com.android.settings.widget.SeekBarPreference; + +import java.util.Objects; + +/** A slider preference that directly controls an BA device volume **/ +public class BADeviceVolumePreference extends SeekBarPreference { + private static final String TAG = "BADeviceVolumePreference"; + + protected SeekBar mSeekBar; + private ImageView mIconView; + + public BADeviceVolumePreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + setLayoutResource(R.layout.preference_ba_device_volume_slider); + } + + public BADeviceVolumePreference(Context context, AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + setLayoutResource(R.layout.preference_ba_device_volume_slider); + } + + public BADeviceVolumePreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.preference_ba_device_volume_slider); + } + + public BADeviceVolumePreference(Context context) { + super(context); + setLayoutResource(R.layout.preference_ba_device_volume_slider); + } +} + diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index aacf41fbbc..4c0a33dca3 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -16,9 +16,11 @@ package com.android.settings.bluetooth; +import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.SystemProperties; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -28,8 +30,11 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; +import com.android.settings.bluetooth.GroupBluetoothProfileSwitchConfirmDialog.BluetoothProfileConfirmListener; + import com.android.settings.R; import com.android.settingslib.bluetooth.A2dpProfile; +import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfile; @@ -40,16 +45,18 @@ import com.android.settingslib.bluetooth.PbapServerProfile; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.List; - /** * This class adds switches for toggling the individual profiles that a Bluetooth device * supports, such as "Phone audio", "Media audio", "Contact sharing", etc. */ public class BluetoothDetailsProfilesController extends BluetoothDetailsController implements Preference.OnPreferenceClickListener, - LocalBluetoothProfileManager.ServiceListener { + LocalBluetoothProfileManager.ServiceListener, BluetoothCallback, + BluetoothProfileConfirmListener { private static final String KEY_PROFILES_GROUP = "bluetooth_profiles"; private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference"; + private static final String BLUETOOTH_PROFILE_CONFIRM_DIALOG_PROP = + "persist.vendor.service.bt.profile_confirm_dialog"; private static final int ORDINAL = 99; @VisibleForTesting @@ -62,6 +69,15 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll @VisibleForTesting PreferenceCategory mProfilesContainer; + private PreferenceFragmentCompat mFragment; + private boolean mIsGroupDevice = false; + private GroupBluetoothProfileSwitchConfirmDialog mGroupBluetoothProfileConfirm; + private int mGroupId = -1; + private GroupUtils mGroupUtils; + private LocalBluetoothProfile mProfile; + private SwitchPreference mProfilePref; + private boolean mIsProfileConfirmDialogSupported = false; + public BluetoothDetailsProfilesController(Context context, PreferenceFragmentCompat fragment, LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle) { super(context, fragment, device, lifecycle); @@ -69,6 +85,14 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mProfileManager = mManager.getProfileManager(); mCachedDevice = device; lifecycle.addObserver(this); + mFragment = fragment; + mGroupUtils = new GroupUtils(context); + mIsGroupDevice = mGroupUtils.isGroupDevice(mCachedDevice); + if (mIsGroupDevice) { + mGroupId = mGroupUtils.getGroupId(mCachedDevice); + } + mIsProfileConfirmDialogSupported = + SystemProperties.getBoolean(BLUETOOTH_PROFILE_CONFIRM_DIALOG_PROP, false); } @Override @@ -170,23 +194,22 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll */ @Override public boolean onPreferenceClick(Preference preference) { - LocalBluetoothProfile profile = mProfileManager.getProfileByName(preference.getKey()); - if (profile == null) { + mProfile = mProfileManager.getProfileByName(preference.getKey()); + if (mProfile == null) { // It might be the PbapServerProfile, which is not stored by name. PbapServerProfile psp = mManager.getProfileManager().getPbapProfile(); if (TextUtils.equals(preference.getKey(), psp.toString())) { - profile = psp; + mProfile = psp; } else { return false; } } - SwitchPreference profilePref = (SwitchPreference) preference; - if (profilePref.isChecked()) { - enableProfile(profile); + mProfilePref = (SwitchPreference) preference; + if (mIsGroupDevice && mIsProfileConfirmDialogSupported) { + showProfileConfirmDialog(); } else { - disableProfile(profile); + enableOrDisableProfile(); } - refreshProfilePreference(profilePref, profile); return true; } @@ -233,7 +256,11 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll highQualityAudioPref.setKey(HIGH_QUALITY_AUDIO_PREF_TAG); highQualityAudioPref.setVisible(false); highQualityAudioPref.setOnPreferenceClickListener(clickedPref -> { + highQualityAudioPref.setEnabled(false); boolean enable = ((SwitchPreference) clickedPref).isChecked(); + if ((a2dp.isMandatoryCodec(device) && !enable) || + (!a2dp.isMandatoryCodec(device) && enable)) + highQualityAudioPref.setEnabled(true); a2dp.setHighQualityAudioEnabled(mCachedDevice.getDevice(), enable); return true; }); @@ -245,12 +272,14 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll public void onPause() { super.onPause(); mProfileManager.removeServiceListener(this); + mManager.getEventManager().unregisterCallback(this); } @Override public void onResume() { super.onResume(); mProfileManager.addServiceListener(this); + mManager.getEventManager().registerCallback(this); } @Override @@ -263,6 +292,41 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll refresh(); } + private void updateA2dpHighQualityAudioPref() { + A2dpProfile a2dp = null; + for (LocalBluetoothProfile profile : getProfiles()) { + if (profile instanceof A2dpProfile) { + if (profile.isProfileReady()) { + a2dp = (A2dpProfile)profile; + } + break; + } + } + if (a2dp == null) { + return; + } + + BluetoothDevice device = mCachedDevice.getDevice(); + SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference( + HIGH_QUALITY_AUDIO_PREF_TAG); + if (highQualityPref != null) { + if (a2dp.isEnabled(device) && a2dp.supportsHighQualityAudio(device)) { + highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device)); + highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device)); + highQualityPref.setEnabled(true); + } + } + } + + @Override + public void onA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, + BluetoothCodecStatus codecStatus) { + if (!cachedDevice.equals(mCachedDevice)) { + return; + } + updateA2dpHighQualityAudioPref(); + } + /** * Refreshes the state of the switches for all profiles, possibly adding or removing switches as * needed. @@ -306,4 +370,47 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll public String getPreferenceKey() { return KEY_PROFILES_GROUP; } -}
\ No newline at end of file + + private void initGroupBluetoothProfileConfirm() { + if (mIsGroupDevice) { + if (mGroupBluetoothProfileConfirm != null) { + mGroupBluetoothProfileConfirm.dismiss(); + mGroupBluetoothProfileConfirm = null; + } + mGroupBluetoothProfileConfirm = + GroupBluetoothProfileSwitchConfirmDialog.newInstance(mGroupId); + mGroupBluetoothProfileConfirm.setPairingController(this); + } + } + + @Override + public void onDialogNegativeClick() { + resetProfileSwitch(); + mGroupBluetoothProfileConfirm.dismiss(); + } + + @Override + public void onDialogPositiveClick() { + enableOrDisableProfile(); + mGroupBluetoothProfileConfirm.dismiss(); + } + + private void showProfileConfirmDialog() { + initGroupBluetoothProfileConfirm(); + mGroupBluetoothProfileConfirm.show(mFragment.getFragmentManager(), + GroupBluetoothProfileSwitchConfirmDialog.TAG); + } + + private void resetProfileSwitch() { + mProfilePref.setChecked(!mProfilePref.isChecked()); + } + + private void enableOrDisableProfile() { + if (mProfilePref.isChecked()) { + enableProfile(mProfile); + } else { + disableProfile(mProfile); + } + refreshProfilePreference(mProfilePref, mProfile); + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index 4980ba313f..c4232e988f 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -23,6 +23,7 @@ import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.Bundle; +import android.os.SystemProperties; import android.provider.DeviceConfig; import android.util.Log; import android.view.Menu; @@ -30,9 +31,12 @@ import android.view.MenuInflater; import android.view.MenuItem; import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.SettingsUIDeviceConfig; +import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.BlockingSlicePrefController; @@ -41,12 +45,20 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment { public static final String KEY_DEVICE_ADDRESS = "device_address"; private static final String TAG = "BTDeviceDetailsFrg"; + private static final String BLUETOOTH_ADV_AUDIO_MASK_PROP + = "persist.vendor.service.bt.adv_audio_mask"; + private static final String BLUETOOTH_BROADCAST_UI_PROP = "persist.bluetooth.broadcast_ui"; + private static final int BA_MASK = 0x02; + private static boolean mBAEnabled = false; + private static boolean mBAPropertyChecked = false; @VisibleForTesting static int EDIT_DEVICE_NAME_ITEM_ID = Menu.FIRST; @@ -126,6 +138,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment use(BlockingSlicePrefController.class).setSliceUri(sliceEnabled ? featureProvider.getBluetoothDeviceSettingsUri(mCachedDevice.getDevice()) : null); + + use(BADeviceVolumeController.class).init(this, mManager, mCachedDevice); } @Override @@ -176,22 +190,72 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment } @Override + protected void displayResourceTilesToScreen(PreferenceScreen screen) { + if (!mBAEnabled || !mCachedDevice.isBASeeker()) { + screen.removePreference(screen.findPreference("sync_helper_buttons")); + screen.removePreference(screen.findPreference("added_sources")); + } + super.displayResourceTilesToScreen(screen); + } + + @Override protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { ArrayList<AbstractPreferenceController> controllers = new ArrayList<>(); - if (mCachedDevice != null) { - Lifecycle lifecycle = getSettingsLifecycle(); - controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice, - lifecycle, mManager)); - controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice, - lifecycle)); - controllers.add(new BluetoothDetailsCompanionAppsController(context, this, - mCachedDevice, lifecycle)); - controllers.add(new BluetoothDetailsProfilesController(context, this, mManager, - mCachedDevice, lifecycle)); - controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, - lifecycle)); + if (mCachedDevice == null) return controllers; + + Lifecycle lifecycle = getSettingsLifecycle(); + controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice, + lifecycle, mManager)); + controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice, + lifecycle)); + controllers.add(new BluetoothDetailsCompanionAppsController(context, this, + mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsProfilesController(context, this, mManager, + mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, + lifecycle)); + if (mBAPropertyChecked == false) { + int advAudioMask = SystemProperties.getInt(BLUETOOTH_ADV_AUDIO_MASK_PROP, 0); + mBAEnabled = (((advAudioMask & BA_MASK) == BA_MASK) && + SystemProperties.getBoolean(BLUETOOTH_BROADCAST_UI_PROP, true)); + mBAPropertyChecked = true; + } + if (mBAEnabled == false) { + return controllers; + } + + Log.d(TAG, "createPreferenceControllers for BA"); + + try { + if (mCachedDevice.isBASeeker()) { + Class<?> classAddSourceController = Class.forName( + "com.android.settings.bluetooth.BluetoothDetailsAddSourceButtonController"); + Class<?> classBADeviceController = Class.forName( + "com.android.settings.bluetooth.BADevicePreferenceController"); + Constructor ctorAddSource = classAddSourceController + .getDeclaredConstructor(new Class[] {Context.class, + PreferenceFragmentCompat.class, CachedBluetoothDevice.class, Lifecycle.class}); + Constructor ctorBADevice = classBADeviceController + .getDeclaredConstructor(new Class[] {Context.class, Lifecycle.class, + String.class}); + Object objAddSourceController = ctorAddSource.newInstance(context, this, + mCachedDevice, lifecycle); + Object objBADeviceController = ctorBADevice.newInstance(context, lifecycle, + "added_sources"); + objBADeviceController.getClass() + .getMethod("init", DashboardFragment.class, CachedBluetoothDevice.class) + .invoke(objBADeviceController, this, mCachedDevice); + controllers.add((AbstractPreferenceController) objAddSourceController); + controllers.add((AbstractPreferenceController) objBADeviceController); + } + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InvocationTargetException | InstantiationException | IllegalArgumentException | + ExceptionInInitializerError e) { + e.printStackTrace(); + mBAEnabled = false; + } finally { + return controllers; } - return controllers; } } diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 3e50049e1e..6751b4038c 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -20,6 +20,7 @@ import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; @@ -92,6 +93,8 @@ public final class BluetoothDevicePreference extends GearPreference { } } + private final boolean mHideSummary; + public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, boolean showDeviceWithoutNames, @SortType int type) { super(context, null); @@ -110,7 +113,28 @@ public final class BluetoothDevicePreference extends GearPreference { mCachedDevice.registerCallback(mCallback); mCurrentTime = System.currentTimeMillis(); mType = type; + onPreferenceAttributesChanged(); + mHideSummary = false; + } + public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, + boolean showDeviceWithoutNames, @SortType int type, boolean hideSummary) { + super(context, null); + mResources = getContext().getResources(); + mUserManager = context.getSystemService(UserManager.class); + mShowDevicesWithoutNames = showDeviceWithoutNames; + if (sDimAlpha == Integer.MIN_VALUE) { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); + sDimAlpha = (int) (outValue.getFloat() * 255); + } + + mCachedDevice = cachedDevice; + mCallback = new BluetoothDevicePreferenceCallback(); + mCachedDevice.registerCallback(mCallback); + mCurrentTime = System.currentTimeMillis(); + mType = type; + mHideSummary = hideSummary; onPreferenceAttributesChanged(); } @@ -185,12 +209,26 @@ public final class BluetoothDevicePreference extends GearPreference { * changed before proceeding. It will also call notifyChanged() if * any preference info has changed from the previous value. */ - setTitle(mCachedDevice.getName()); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && + mCachedDevice.getAddress().equals(adapter.getAddress())) { + //for ba related things, using the same preference + //for showing the local device + setTitle(adapter.getName()+"(self)"); + } else { + setTitle(mCachedDevice.getName()); + } // Null check is done at the framework - setSummary(mCachedDevice.getConnectionSummary()); + if (!mHideSummary) { + setSummary(mCachedDevice.getConnectionSummary()); + } // Used to gray out the item - setEnabled(!mCachedDevice.isBusy()); + if (mHideSummary) { + setEnabled(true); + } else { + setEnabled(!mCachedDevice.isBusy()); + } // Device is only visible in the UI if it has a valid name besides MAC address or when user // allows showing devices without user-friendly name in developer settings diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java index d65500be27..b514ec7125 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java @@ -40,6 +40,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.UUID; /** * Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using @@ -62,8 +63,10 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, @VisibleForTesting protected LocalBluetoothManager mLocalManager; + private GroupUtils mGroupUtils; + @VisibleForTesting - final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> { + public final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> { launchDeviceDetails(pref); }; @@ -80,6 +83,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, mPreferenceMap = new HashMap<>(); mLocalManager = localManager; mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + mGroupUtils = new GroupUtils(context); } /** @@ -187,6 +191,16 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, } @Override + public void onNewGroupFound(CachedBluetoothDevice cachedDevice, int groupId, + UUID setPrimaryServiceUuid) { + if (DBG) { + Log.d(TAG, " NewGroupFound device: " + cachedDevice + + ", groupId: " + groupId); + } + update(cachedDevice); + } + + @Override public void onServiceConnected() { // When bluetooth service connected update the UI forceUpdate(); @@ -326,4 +340,17 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback, ((BluetoothDevicePreference) preference).onPreferenceAttributesChanged(); } } + + public boolean isGroupDevice(CachedBluetoothDevice cachedDevice) { + return mGroupUtils.isGroupDevice(cachedDevice); + } + + public boolean isPrivateAddr(CachedBluetoothDevice cachedDevice) { + boolean isPvtAddr = cachedDevice.isPrivateAddr(); + if (DBG) { + Log.d(TAG, "isPrivateAddr device name : " + cachedDevice.getName() + + " isPvtAddr " + isPvtAddr); + } + return isPvtAddr; + } } diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index d489198103..be6cf66a0f 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -136,19 +136,19 @@ public final class BluetoothEnabler implements SwitchWidgetController.OnSwitchCh mSwitchController.setEnabled(false); break; case BluetoothAdapter.STATE_ON: - setChecked(true); mSwitchController.setEnabled(true); + setChecked(true); break; case BluetoothAdapter.STATE_TURNING_OFF: mSwitchController.setEnabled(false); break; case BluetoothAdapter.STATE_OFF: - setChecked(false); mSwitchController.setEnabled(true); + setChecked(false); break; default: - setChecked(false); mSwitchController.setEnabled(true); + setChecked(false); } } diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java index 74c39b6f8c..7848fa4d86 100644 --- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java @@ -89,7 +89,7 @@ abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment .setTitle(getDialogTitle()) .setView(createDialogView(deviceName)) .setPositiveButton(R.string.bluetooth_rename_button, (dialog, which) -> { - setDeviceName(mDeviceNameView.getText().toString()); + setDeviceName(mDeviceNameView.getText().toString().trim()); }) .setNegativeButton(android.R.string.cancel, null); mAlertDialog = builder.create(); @@ -133,7 +133,11 @@ abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_DONE) { - setDeviceName(v.getText().toString()); + // Rejecting Empty String + if (v.length() != 0 && !(v.getText().toString().trim().isEmpty())) + { + setDeviceName(v.getText().toString()); + } if (mAlertDialog != null && mAlertDialog.isShowing()) { mAlertDialog.dismiss(); } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java index ca3dda6738..3bce36b9ec 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingController.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java @@ -63,7 +63,6 @@ public class BluetoothPairingController implements OnCheckedChangeListener, private int mInitiator; private String mDeviceName; private LocalBluetoothProfile mPbapClientProfile; - private boolean mPbapAllowed; /** * Creates an instance of a BluetoothPairingController. @@ -95,20 +94,14 @@ public class BluetoothPairingController implements OnCheckedChangeListener, @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { - mPbapAllowed = true; + mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); } else { - mPbapAllowed = false; + mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); } } @Override public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) { - if (mPbapAllowed) { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); - } else { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); - } - if (getDialogType() == USER_ENTRY_DIALOG) { onPair(mUserInput); } else { diff --git a/src/com/android/settings/bluetooth/BluetoothPairingService.java b/src/com/android/settings/bluetooth/BluetoothPairingService.java index bc5dc6695d..40d446a975 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingService.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingService.java @@ -200,7 +200,12 @@ public final class BluetoothPairingService extends Service { String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); if (TextUtils.isEmpty(name)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - name = device != null ? device.getAlias() : res.getString(android.R.string.unknownName); + if (device != null) { + name = device.getAlias() != null ? device.getAlias(): + device.getAddress(); + } else { + name = res.getString(android.R.string.unknownName); + } } Log.d(TAG, "Show pairing notification for " + " (" + name + ")"); diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java index fc1b9b734b..31d9b1e3e6 100644 --- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java @@ -94,6 +94,19 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { Log.d(TAG, "isFilterMatched() device : " + cachedDevice.getName() + ", isFilterMatched : " + isFilterMatched); } + if (isFilterMatched) { + if (isGroupDevice(cachedDevice)) { + isFilterMatched = false; + if (DBG) { + Log.d(TAG, "It is isGroupDevice ignore showing "); + } + } else if (isPrivateAddr(cachedDevice)) { + isFilterMatched = false; + if (DBG) { + Log.d(TAG, "It is isPrivateAddr ignore showing "); + } + } + } } return isFilterMatched; } diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java index 602b79b2ae..382dee4487 100644 --- a/src/com/android/settings/bluetooth/DevicePickerFragment.java +++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java @@ -204,6 +204,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { } private void sendDevicePickedIntent(BluetoothDevice device) { + android.util.Log.d("Devicepicker", "sendDevicePickedIntent"); Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); if (mLaunchPackage != null && mLaunchClass != null) { @@ -211,6 +212,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { intent.setClassName(mLaunchPackage, mLaunchClass); } } + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent, Manifest.permission.BLUETOOTH_CONNECT); } diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDetailsButtonsController.java b/src/com/android/settings/bluetooth/GroupBluetoothDetailsButtonsController.java new file mode 100644 index 0000000000..84ec93f9ab --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDetailsButtonsController.java @@ -0,0 +1,332 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothDeviceGroup; +import android.content.Context; +import android.util.Log; +import android.view.View; + +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import java.util.ArrayList; +import java.util.List; + +import com.android.settings.R; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.widget.GroupOptionsPreference; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * This class adds Group action buttons: one to connect/disconnect from a device + * (depending on the current connected state of all devices in this group ), and + * one to "forget" (ie unpair) the device and one for to refresh devices. +*/ +public class GroupBluetoothDetailsButtonsController extends GroupBluetoothDetailsController { + + private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + private static String TAG = "GroupBluetoothDetailsButtonsController"; + private static final String KEY_GROUP_OPTIONS = "group_options"; + private boolean mConnectButtonInitialized; + private GroupOptionsPreference mGroupOptions; + private boolean mIsUpdate = false; + private int mGroupId; + private GroupUtils mGroupUtils; + private int mGroupSize = -1; + private int mDiscoveredSize = 0; + private boolean isRefreshClicked = false; + private ArrayList<CachedBluetoothDevice> mDevicesList = new ArrayList<CachedBluetoothDevice>(); + + + public GroupBluetoothDetailsButtonsController(Context context, + PreferenceFragmentCompat fragment, int groupId, Lifecycle lifecycle) { + super(context, fragment, groupId, lifecycle); + mGroupId = groupId; + mGroupUtils = new GroupUtils(context); + mGroupSize = mGroupUtils.getGroupSize(mGroupId); + } + + @Override + protected void init(PreferenceScreen screen) { + if (DBG) { + Log.d(TAG, "init "); + } + mGroupOptions = ((GroupOptionsPreference) + screen.findPreference(getPreferenceKey())); + mGroupOptions.setTextViewText(mContext.getString(R.string.group_id) + + mGroupUtils.getGroupTitle(mGroupId)); + mGroupOptions.setForgetButtonText(R.string.forget_group); + mGroupOptions.setForgetButtonOnClickListener((View) -> onForgetButtonPressed()); + mGroupOptions.setForgetButtonEnabled(true); + mGroupOptions.setTexStatusText(R.string.active); + mGroupOptions.setConnectButtonText(R.string.connect_group); + mGroupOptions.setConnectButtonOnClickListener((View) -> onConnectButtonPressed()); + mGroupOptions.setDisconnectButtonText(R.string.disconnect_group); + mGroupOptions.setDisconnectButtonOnClickListener((View) -> onDisConnectButtonPressed()); + mGroupOptions.setCancelRefreshButtonText(R.string.cancel_refresh_group); + mGroupOptions.setCancelRefreshButtonOnClickListener( + (View) -> onCacelRefreshButtonPressed()); + mGroupOptions.setCancelRefreshButtonVisible(false); + mGroupOptions.setRefreshButtonText(R.string.refresh_group); + mGroupOptions.setRefreshButtonOnClickListener((View) -> onRefreshButtonPressed()); + mGroupOptions.setRefreshButtonVisible(false); + mIsUpdate = true; + + BluetoothDevice bcMemberDevice = mGroupUtils.getAnyBCConnectedDevice(mGroupId); + boolean enableASButton = false; + if (bcMemberDevice != null) { + enableASButton = true; + } + mGroupOptions.setAddSourceGroupButtonText(R.string.add_source_group); + mGroupOptions.setAddSourceGroupButtonEnabled(enableASButton); + mGroupOptions.setAddSourceGroupButtonVisible(enableASButton); + if (enableASButton) { + mGroupOptions.setAddSourceGroupButtonOnClickListener((View) -> + onAddSourceGroupButtonPressed()); + } + } + + private void onAddSourceGroupButtonPressed() { + mGroupUtils.launchAddSourceGroup(mGroupId); + } + + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { + if (DBG) { + Log.d(TAG, "onConnectionStateChanged cachedDevice "+cachedDevice +" state "+state); + } + if (mGroupUtils.isUpdate(mGroupId, cachedDevice)) { + refresh(); + } + } + + @Override + public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, + int state, int bluetoothProfile) { + if (DBG) { + Log.d(TAG, "onProfileConnectionStateChanged cachedDevice " + cachedDevice + + " state " + state); + } + if (mGroupUtils.isUpdate(mGroupId, cachedDevice)) { + refresh(); + } + } + + @Override + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + boolean isUpdated = false; + if (bondState == BluetoothDevice.BOND_BONDED) { + isUpdated = mGroupUtils.addDevice(mDevicesList, mGroupId, cachedDevice); + } else if (bondState == BluetoothDevice.BOND_NONE) { + isUpdated = mGroupUtils.removeDevice(mDevicesList, mGroupId, cachedDevice); + } + if (isUpdated) { + mDiscoveredSize = mDevicesList.size(); + } + if (DBG) { + Log.d(TAG, "onDeviceBondStateChanged cachedDevice " + cachedDevice + + " name " + cachedDevice.getName() + " bondState " + bondState + +" isUpdated " + isUpdated + " mDiscoveredSize " + mDiscoveredSize); + } + if (isUpdated) { + updateProgressScan(); + refresh(); + } + } + @Override + public void onStop() { + if (DBG) { + Log.d(TAG, "onStop "); + } + super.onStop(); + disableScanning(); + } + + @Override + public void onGroupDiscoveryStatusChanged (int groupId, int status, int reason) { + if (DBG) { + Log.d(TAG, "onSetDiscoveryStatusChanged " + groupId + " status :" + status + + " Reason :" + reason); + } + if (groupId == mGroupId) { + if (isRefreshClicked && status == BluetoothDeviceGroup.GROUP_DISCOVERY_STOPPED ) { + isRefreshClicked = false; + } + updateProgressScan(); + } + } + + @Override + protected void loadDevices() { + mDevicesList = mGroupUtils.getCahcedDevice(mGroupId); + mDiscoveredSize = mDevicesList.size(); + if (DBG) { + Log.d(TAG, "loadDevices mGroupId " + mGroupId + " mGroupSize " + mGroupSize + + " mDiscoveredSize " + mDiscoveredSize); + } + updateProgressScan(); + } + + @Override + protected void refresh() { + boolean isBusy = false; + boolean showConnect = false; + boolean showDisconnect = false; + boolean isActive = false; + List<CachedBluetoothDevice> devicesList = new ArrayList<>(mDevicesList); + if (DBG) { + Log.d(TAG, "updateFlags list " + devicesList + " size " + devicesList.size()); + } + for (CachedBluetoothDevice cacheDevice : devicesList) { + if (DBG) { + Log.d(TAG, "refresh cacheDevice " + cacheDevice + " connected " + + cacheDevice.isConnected() +" busy " + cacheDevice.isBusy()); + } + if (!isBusy && cacheDevice.isBusy()) { + isBusy = true; + mIsUpdate = true; + } + if (!showDisconnect && cacheDevice.isConnected()) { + showDisconnect = true; + mIsUpdate = true; + } else if (!showConnect && !cacheDevice.isConnected()) { + showConnect = true; + mIsUpdate = true; + } if((!isActive) && cacheDevice.isConnected() + && (cacheDevice.isActiveDevice(BluetoothProfile.A2DP) + || cacheDevice.isActiveDevice(BluetoothProfile.HEADSET) + || cacheDevice.isActiveDevice(BluetoothProfile.HEARING_AID))) { + isActive = true; + } + } + if (DBG) { + Log.d(TAG, "refresh isBusy " + isBusy + " showConnect " + showConnect + + " showDisconnect :" + showDisconnect +" isActive " + isActive + + " mIsUpdate " + mIsUpdate); + } + if (!mIsUpdate) { + return; + } + mGroupOptions.setConnectButtonEnabled(!isBusy); + mGroupOptions.setDisconnectButtonEnabled(!isBusy); + mGroupOptions.setRefreshButtonEnabled(!isBusy); + mGroupOptions.setCancelRefreshButtonEnabled(!isBusy); + mGroupOptions.setDisconnectButtonEnabled(showDisconnect); + mGroupOptions.setDisconnectButtonVisible(showDisconnect); + mGroupOptions.setConnectButtonEnabled(showConnect); + mGroupOptions.setConnectButtonVisible(showConnect); + mGroupOptions.setTvStatusVisible(isActive); + mIsUpdate = false; + } + + @Override + public String getPreferenceKey() { + return KEY_GROUP_OPTIONS; + } + + private void onForgetButtonPressed() { + if (DBG) { + Log.d(TAG, "onForgetButtonPressed"); + } + GroupForgetDialogFragment fragment = GroupForgetDialogFragment.newInstance(mGroupId); + fragment.show(mFragment.getFragmentManager(), GroupForgetDialogFragment.TAG); + } + + private void onConnectButtonPressed() { + disableScanning(); + boolean connect = mGroupUtils.connectGroup(mGroupId); + if (DBG) { + Log.d(TAG, "onConnectButtonPressed connect " + connect); + } + } + + private void onDisConnectButtonPressed() { + disableScanning(); + boolean disconnect = mGroupUtils.disconnectGroup(mGroupId); + if (DBG) { + Log.d(TAG, "onDisConnectButtonPressed disconnect " + disconnect); + } + } + + private void onRefreshButtonPressed() { + isRefreshClicked = mGroupUtils.startGroupDiscovery(mGroupId); + if (DBG) { + Log.d(TAG, "onRefreshButtonPressed isRefreshClicked " + isRefreshClicked); + } + + } + + private void onCacelRefreshButtonPressed() { + isRefreshClicked = false; + boolean stopDiscovery = mGroupUtils.stopGroupDiscovery(mGroupId); + if (DBG) { + Log.d(TAG, "onCacelRefreshButtonPressed stopDiscovery " + stopDiscovery); + } + } + + private void updateProgressScan() { + boolean showRefresh = false; + if ((mGroupSize >0) && (mGroupSize > mDiscoveredSize)) { + showRefresh = true; + } + boolean isRefreshing = mGroupUtils.isGroupDiscoveryInProgress(mGroupId); + if (showRefresh) { + if (isRefreshing) { + mGroupOptions.setProgressScanVisible(true); + mGroupOptions.setRefreshButtonVisible(false); + mGroupOptions.setCancelRefreshButtonVisible(true); + } else { + mGroupOptions.setProgressScanVisible(false); + mGroupOptions.setRefreshButtonVisible(true); + mGroupOptions.setCancelRefreshButtonVisible(false); + } + } else { + mGroupOptions.setProgressScanVisible(showRefresh); + mGroupOptions.setCancelRefreshButtonVisible(showRefresh); + mGroupOptions.setRefreshButtonVisible(showRefresh); + } + if (DBG) { + Log.d(TAG, "updateProgressScan showRefresh " + showRefresh + + ", isRefreshing " + isRefreshing + " mDiscoveredSize " + mDiscoveredSize); + } + } + + private void disableScanning () { + if (isRefreshClicked) { + mGroupUtils.stopGroupDiscovery(mGroupId); + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDetailsController.java b/src/com/android/settings/bluetooth/GroupBluetoothDetailsController.java new file mode 100644 index 0000000000..431dc84130 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDetailsController.java @@ -0,0 +1,101 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import android.content.Context; + +public abstract class GroupBluetoothDetailsController extends AbstractPreferenceController + implements PreferenceControllerMixin, LifecycleObserver, + OnStop, OnStart, BluetoothCallback { + + protected final Context mContext; + protected final PreferenceFragmentCompat mFragment; + protected LocalBluetoothManager mLocalManager; + + + public GroupBluetoothDetailsController(Context context, PreferenceFragmentCompat fragment, + int groupId, Lifecycle lifecycle) { + super(context); + mContext = context; + mFragment = fragment; + lifecycle.addObserver(this); + mLocalManager = Utils.getLocalBtManager(mContext); + } + + @Override + public void onStart() { + mLocalManager.getEventManager().registerCallback(this); + loadDevices(); + refresh(); + } + + @Override + public void onStop() { + mLocalManager.getEventManager().unregisterCallback(this); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public final void displayPreference(PreferenceScreen screen) { + init(screen); + super.displayPreference(screen); + } + + /** + * This is a method to do one-time initialization when the screen is first created, such as + * adding preferences. + * @param screen the screen where this controller's preferences should be added + */ + protected abstract void init(PreferenceScreen screen); + + /** + * This method is called when something about the bluetooth device has changed, and this object + * should update the preferences it manages based on the new state. + */ + protected abstract void refresh(); + + protected abstract void loadDevices(); +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/GroupBluetoothDeviceUpdater.java new file mode 100644 index 0000000000..1284f223c0 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDeviceUpdater.java @@ -0,0 +1,96 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.bluetooth.BluetoothDevicePreference; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.BluetoothDeviceFilter; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Update the Group bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} + * using {@link BluetoothCallback}. It notifies the upper level whether to add/remove the + * preference through {@link DevicePreferenceCallback} + * + * In {@link GroupBluetoothDeviceUpdater}, it uses {@link BluetoothDeviceFilter.Filter} to detect + * whether the {@link CachedBluetoothDevice} is relevant. + */ +public abstract class GroupBluetoothDeviceUpdater extends BluetoothDeviceUpdater { + private static final String TAG = "GroupBluetoothDeviceUpdater"; + protected static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + + public GroupBluetoothDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(context, fragment, devicePreferenceCallback); + } + + @Override + protected void addPreference(CachedBluetoothDevice cachedDevice) { + addPreference(cachedDevice, BluetoothDevicePreference.SortType.TYPE_NO_SORT); + } + + public void launchgroupOptions(Preference preference) { + if (DBG) + Log.d(TAG, " launchgroupOptions :" + preference); + mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory()); + final GroupBluetoothSettingsPreference pref = + (GroupBluetoothSettingsPreference) preference; + final Bundle args = new Bundle(); + args.putInt(GroupBluetoothFragment.KEY_GROUP_ID, pref.getGroupId()); + new SubSettingLauncher(mFragment.getContext()).setDestination + (GroupBluetoothFragment.class.getName()) + .setArguments(args).setTitleRes(R.string.group_options) + .setSourceMetricsCategory(mFragment.getMetricsCategory()).launch(); + } + +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaController.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaController.java new file mode 100644 index 0000000000..8a4eaa29f2 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaController.java @@ -0,0 +1,134 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.preference.PreferenceFragmentCompat; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupSavedBluetoothDeviceUpdater; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.core.lifecycle.events.OnResume; + +import android.util.Log; +import com.android.settings.R; + +/** + * Controller to maintain available Group devices + */ +public class GroupBluetoothDevicesAvailableMediaController extends BasePreferenceController + implements PreferenceControllerMixin, + LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { + + private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + private static final String KEY = "group_options_active_devices"; + private static final String TAG = "GroupBluetoothDevicesAvailableMediaController"; + private PreferenceGroup mPreferenceGroup; + private GroupBluetoothDevicesAvailableMediaDeviceUpdater mGroupDeviceUpdater; + private int mGroupId; + + public GroupBluetoothDevicesAvailableMediaController(Context context, DashboardFragment + fragment, Lifecycle lifecycle, int groupId) { + super(context, KEY); + mGroupId = groupId; + lifecycle.addObserver(this); + mGroupDeviceUpdater = new GroupBluetoothDevicesAvailableMediaDeviceUpdater + (fragment.getContext(), fragment, this, mGroupId); + } + + @Override + public void onStart() { + mGroupDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mGroupDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + mPreferenceGroup = screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + if (isAvailable()) { + final Context context = screen.getContext(); + mGroupDeviceUpdater.setPrefContext(context); + mGroupDeviceUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) ? + AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } + +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaDeviceUpdater.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaDeviceUpdater.java new file mode 100644 index 0000000000..770abac5a3 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesAvailableMediaDeviceUpdater.java @@ -0,0 +1,117 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ +package com.android.settings.bluetooth; + +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; + +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.media.AudioManager; +import android.util.Log; + +/** + * Controller to maintain available Group devices + */ +public class GroupBluetoothDevicesAvailableMediaDeviceUpdater extends + GroupBluetoothGroupDeviceUpdater { + + private static final String TAG = "GroupBluetoothDevicesAvailableMediaDeviceUpdater"; + private static final String PREF_KEY = "group_options_active_devices"; + private int mGroupId; + private final AudioManager mAudioManager; + private GroupUtils mGroupUtils; + + public GroupBluetoothDevicesAvailableMediaDeviceUpdater(Context context, DashboardFragment + fragment, DevicePreferenceCallback devicePreferenceCallback, int groupId) { + super(context, fragment, devicePreferenceCallback); + mGroupId = groupId; + mAudioManager = context.getSystemService(AudioManager.class); + mGroupUtils = new GroupUtils(context); + } + + @Override + public void onAudioModeChanged() { + forceUpdate(); + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + final int audioMode = mAudioManager.getMode(); + final int currentAudioProfile; + if (audioMode == AudioManager.MODE_RINGTONE || audioMode == AudioManager.MODE_IN_CALL + || audioMode == AudioManager.MODE_IN_COMMUNICATION) { + // in phone call + currentAudioProfile = BluetoothProfile.HEADSET; + } else { + // without phone call + currentAudioProfile = BluetoothProfile.A2DP; + } + boolean isFilterMatched = false; + if (isDeviceConnected(cachedDevice)) { + if (DBG) { + Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); + } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would not show in Connected Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return false; + } + // According to the current audio profile type, + // this page will show the bluetooth device that doesn't have corresponding + // profile. + // For example: + // If current audio profile is a2dp, + // show the bluetooth device that doesn't have a2dp profile. + // If current audio profile is headset, + // show the bluetooth device that doesn't have headset profile. + switch (currentAudioProfile) { + case BluetoothProfile.A2DP: + isFilterMatched = cachedDevice.isConnectedA2dpDevice(); + break; + case BluetoothProfile.HEADSET: + isFilterMatched = cachedDevice.isConnectedHfpDevice(); + break; + } + if (DBG) { + Log.d(TAG, "isFilterMatched " + isFilterMatched + + " cachedDevice : " + cachedDevice ); + } + } + return isFilterMatched && isGroupDevice(cachedDevice) + && mGroupId == mGroupUtils.getGroupId(cachedDevice); + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedController.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedController.java new file mode 100644 index 0000000000..f866ee0264 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedController.java @@ -0,0 +1,129 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.preference.PreferenceFragmentCompat; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupSavedBluetoothDeviceUpdater; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.core.lifecycle.events.OnResume; + +/** + * Maintain and update saved group devices(bonded but not connected) + */ +public class GroupBluetoothDevicesBondedController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback { + + private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + private static final String KEY = "group_options_bonded_devices"; + private static final String TAG = "GroupBluetoothDevicesBondedController"; + private PreferenceGroup mPreferenceGroup; + private GroupBluetoothDevicesBondedUpdater mGroupDeviceUpdater; + private int mGroupId; + + public GroupBluetoothDevicesBondedController(Context context, DashboardFragment fragment, + Lifecycle lifecycle, int groupId) { + super(context, KEY); + mGroupId = groupId; + lifecycle.addObserver(this); + mGroupDeviceUpdater = new GroupBluetoothDevicesBondedUpdater(fragment.getContext(), + fragment, this, mGroupId); + } + + @Override + public void onStart() { + mGroupDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mGroupDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + mPreferenceGroup = screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + if (isAvailable()) { + final Context context = screen.getContext(); + mGroupDeviceUpdater.setPrefContext(context); + mGroupDeviceUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) ? + AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedUpdater.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedUpdater.java new file mode 100644 index 0000000000..5652fcf397 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesBondedUpdater.java @@ -0,0 +1,92 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Maintain and update saved group devices(bonded but not connected) + */ +public class GroupBluetoothDevicesBondedUpdater extends GroupBluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { + + private static final String TAG = "GroupBluetoothDevicesBondedUpdater"; + private static final String PREF_KEY = "group_options_bonded_devices_updater"; + private int mGroupId; + private GroupUtils mGroupUtils; + + public GroupBluetoothDevicesBondedUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback, int groupId) { + super(context, fragment, devicePreferenceCallback); + mGroupUtils = new GroupUtils(context); + mGroupId = groupId; + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + int groupId = -1; + if (DBG) { + Log.d(TAG, "isFilterMatched " + cachedDevice + "bond state " + device.getBondState() + + " mGroupId " + mGroupId); + } + return (device.getBondState() == BluetoothDevice.BOND_BONDED) + && !device.isConnected() && isGroupDevice(cachedDevice) + && mGroupId == mGroupUtils.getGroupId(cachedDevice); + } + + @Override + public boolean onPreferenceClick(Preference preference) { + mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory()); + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + device.connect(); + return true; + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedController.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedController.java new file mode 100644 index 0000000000..cc3d89906d --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedController.java @@ -0,0 +1,127 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.preference.PreferenceFragmentCompat; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.core.lifecycle.events.OnResume; + +/** + * Controller to maintain connected Group devices (Connected but not active) + */ +public class GroupBluetoothDevicesConnectedController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback { + + private static final String KEY = "group_options_connected_devices"; + private static final String TAG = "GroupBluetoothDevicesConnectedController"; + private PreferenceGroup mPreferenceGroup; + private GroupBluetoothDevicesConnectedUpdater mGroupDeviceUpdater; + private int mGroupId; + + public GroupBluetoothDevicesConnectedController(Context context, DashboardFragment fragment, + Lifecycle lifecycle, int groupid) { + super(context, KEY); + mGroupId = groupid; + lifecycle.addObserver(this); + mGroupDeviceUpdater = new GroupBluetoothDevicesConnectedUpdater(fragment.getContext(), + fragment, this, mGroupId); + } + + @Override + public void onStart() { + mGroupDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mGroupDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + mPreferenceGroup = screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + if (isAvailable()) { + final Context context = screen.getContext(); + mGroupDeviceUpdater.setPrefContext(context); + mGroupDeviceUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + return (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH)) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedUpdater.java b/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedUpdater.java new file mode 100644 index 0000000000..1e28c9594b --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothDevicesConnectedUpdater.java @@ -0,0 +1,135 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; + +import androidx.preference.Preference; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.media.AudioManager; +import android.util.Log; + +/** + * Controller to maintain connected ( not active) Group devices +*/ + +public class GroupBluetoothDevicesConnectedUpdater extends GroupBluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { + + private static final String TAG = "GroupBluetoothDevicesConnectedUpdater"; + private static final String PREF_KEY = "group_devices_connected"; + private int mGroupId; + private final AudioManager mAudioManager; + private GroupUtils mGroupUtils; + + public GroupBluetoothDevicesConnectedUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback, int groupId) { + super(context, fragment, devicePreferenceCallback); + mGroupId = groupId; + mAudioManager = context.getSystemService(AudioManager.class); + mGroupUtils = new GroupUtils(context); + } + + @Override + public void onAudioModeChanged() { + if (DBG) { + Log.d(TAG, "onAudioModeChanged "); + } + forceUpdate(); + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + final int audioMode = mAudioManager.getMode(); + final int currentAudioProfile; + if (audioMode == AudioManager.MODE_RINGTONE || audioMode == AudioManager.MODE_IN_CALL + || audioMode == AudioManager.MODE_IN_COMMUNICATION) { + // in phone call + currentAudioProfile = BluetoothProfile.HEADSET; + } else { + // without phone call + currentAudioProfile = BluetoothProfile.A2DP; + } + boolean isFilterMatched = false; + if (isDeviceConnected(cachedDevice)) { + if (DBG) { + Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); + } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would not show in Connected Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return false; + } + // According to the current audio profile type, + // this page will show the bluetooth device that doesn't have corresponding + // profile. + // For example: + // If current audio profile is a2dp, + // show the bluetooth device that doesn't have a2dp profile. + // If current audio profile is headset, + // show the bluetooth device that doesn't have headset profile. + switch (currentAudioProfile) { + case BluetoothProfile.A2DP: + isFilterMatched = !cachedDevice.isConnectedA2dpDevice(); + break; + case BluetoothProfile.HEADSET: + isFilterMatched = !cachedDevice.isConnectedHfpDevice(); + break; + } + } + if (DBG) { + Log.d(TAG, "isFilterMatche cachedDevice : " + cachedDevice +" name " + + cachedDevice.getName() + ", isFilterMatched : " + isFilterMatched ); + } + + return isFilterMatched && isGroupDevice(cachedDevice) + && mGroupId == mGroupUtils.getGroupId(cachedDevice); + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + if (DBG) { + Log.d(TAG, "onPreferenceClick " + preference); + } + mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory()); + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + return device.setActive(); + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothFragment.java b/src/com/android/settings/bluetooth/GroupBluetoothFragment.java new file mode 100644 index 0000000000..84033c4d60 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothFragment.java @@ -0,0 +1,158 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; + +import java.util.ArrayList; +import java.util.List; + +import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.instrumentation.Instrumentable; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import com.android.settings.R; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; + +/* + * Fragment to view Group related components +*/ +public class GroupBluetoothFragment extends RestrictedDashboardFragment + implements BluetoothCallback { + + private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + private static final String TAG = "GroupBluetoothFragment"; + static final String KEY_GROUP_ID = "group_id"; + private int mGroupId = -1; + private Context mCtx; + private GroupUtils mGroupUtils; + protected LocalBluetoothManager mLocalManager; + + public GroupBluetoothFragment() { + super(DISALLOW_CONFIG_BLUETOOTH); + } + + public static GroupBluetoothFragment newInstance(int groupId) { + Bundle args = new Bundle(1); + args.putInt(KEY_GROUP_ID, groupId); + GroupBluetoothFragment fragment = new GroupBluetoothFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + mGroupId = getArguments().getInt(KEY_GROUP_ID); + mCtx = context; + mGroupUtils = new GroupUtils(mCtx); + mLocalManager = Utils.getLocalBtManager(mCtx); + mLocalManager.getEventManager().registerCallback(this); + super.onAttach(context); + if (mGroupId <= -1) { + // Close this page if groupId is not valid + Log.w(TAG, "onAttach mGroupId not valid " + mGroupId); + finish(); + return; + } + use(GroupBluetoothDetailsButtonsController.class); + } + + @Override + public void onResume() { + super.onResume(); + finishFragmentIfNecessary(); + } + + @Override + public void onDetach() { + super.onDetach(); + mLocalManager.getEventManager().unregisterCallback(this); + } + + void finishFragmentIfNecessary() { + if (mGroupId < 0 || mGroupUtils.isHideGroupOptions(mGroupId)) { + Log.w(TAG, "finishFragment"); + finish(); + return; + } + } + + @Override + public int getMetricsCategory() { + return Instrumentable.METRICS_CATEGORY_UNKNOWN; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.bluetooth_group_details_fragment; + } + + @Override + protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { + if (DBG) { + Log.d(TAG, "createPreferenceControllers mGroupId " + mGroupId); + } + ArrayList<AbstractPreferenceController> controllers = new ArrayList<>(); + Lifecycle lifecycle = getSettingsLifecycle(); + controllers.add(new GroupBluetoothDetailsButtonsController(context, + this, mGroupId, lifecycle)); + controllers.add(new GroupBluetoothDevicesAvailableMediaController( + context, this, lifecycle, mGroupId)); + controllers.add(new GroupBluetoothDevicesConnectedController( + context, this, lifecycle, mGroupId)); + controllers.add(new GroupBluetoothDevicesBondedController( + context, this, lifecycle, mGroupId)); + return controllers; + } + + @Override + public void onBluetoothStateChanged(int bluetoothState) { + if (DBG) { + Log.d(TAG, "onBluetoothStateChanged bluetoothState " + bluetoothState); + } + if (BluetoothAdapter.STATE_TURNING_OFF == bluetoothState) { + finish(); + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothGroupDeviceUpdater.java b/src/com/android/settings/bluetooth/GroupBluetoothGroupDeviceUpdater.java new file mode 100644 index 0000000000..9634d5bb20 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothGroupDeviceUpdater.java @@ -0,0 +1,77 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.BluetoothDeviceFilter; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public abstract class GroupBluetoothGroupDeviceUpdater extends GroupBluetoothDeviceUpdater { + + public GroupBluetoothGroupDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(context, fragment, devicePreferenceCallback); + } + + @Override + protected void addPreference(CachedBluetoothDevice cachedDevice, + @BluetoothDevicePreference.SortType int type) { + final BluetoothDevice device = cachedDevice.getDevice(); + if (!mPreferenceMap.containsKey(device)) { + BluetoothDevicePreference btPreference = new BluetoothDevicePreference(mPrefContext, + cachedDevice, true /* showDeviceWithoutNames */, type, true); + btPreference.setOnGearClickListener(mDeviceProfilesListener); + mPreferenceMap.put(device, btPreference); + mDevicePreferenceCallback.onDeviceAdded(btPreference); + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothProfileSwitchConfirmDialog.java b/src/com/android/settings/bluetooth/GroupBluetoothProfileSwitchConfirmDialog.java new file mode 100644 index 0000000000..dd70b39ac3 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothProfileSwitchConfirmDialog.java @@ -0,0 +1,129 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import android.util.Log; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import com.android.settingslib.core.instrumentation.Instrumentable; + +import com.android.settings.R; +import androidx.appcompat.app.AlertDialog; + +public class GroupBluetoothProfileSwitchConfirmDialog extends InstrumentedDialogFragment { + + static final String TAG = "GroupBluetoothProfileSwitchConfirmDialog"; + private static final String KEY_GROUP_ID ="group_id"; + private int mGroupId = -1; + private GroupUtils mGroupUtils; + private BluetoothDetailsProfilesController mProfileController; + + public interface BluetoothProfileConfirmListener { + + void onDialogNegativeClick(); + + void onDialogPositiveClick(); + } + + public static GroupBluetoothProfileSwitchConfirmDialog newInstance(int groupId) { + Bundle args = new Bundle(1); + args.putInt(KEY_GROUP_ID, groupId); + GroupBluetoothProfileSwitchConfirmDialog dialog = new + GroupBluetoothProfileSwitchConfirmDialog(); + dialog.setArguments(args); + return dialog; + } + + @Override + public int getMetricsCategory() { + return Instrumentable.METRICS_CATEGORY_UNKNOWN; + } + + String getGroupTitle() { + mGroupId = getArguments().getInt(KEY_GROUP_ID); + return mGroupUtils.getGroupTitle(mGroupId); + } + + @Override + public Dialog onCreateDialog(Bundle inState) { + DialogInterface.OnClickListener onConfirm = (dialog, which) -> { + onPositiveButtonClicked(); + dialog.dismiss(); + }; + DialogInterface.OnClickListener onCancel = (dialog, which) -> { + onNegativeButtonClicked(); + dialog.dismiss(); + }; + + Context context = getContext(); + mGroupUtils = new GroupUtils(context); + AlertDialog dialog = new AlertDialog.Builder(context) + .setPositiveButton(R.string.group_confirm_dialog_apply_button, onConfirm) + .setNegativeButton(android.R.string.cancel, onCancel).create(); + dialog.setTitle(R.string.group_apply_changes_dialog_title); + dialog.setMessage(context.getString(R.string.group_confirm_dialog_body, getGroupTitle())); + return dialog; + } + + /** + * Sets the controller that the fragment should use. this method MUST be called + * before you try to show the dialog or an error will be thrown. An implementation + * of a pairing controller can be found at {@link BluetoothPairingController}. A + * controller may not be substituted once it is assigned. Forcibly switching a + * controller for a new one will lead to undefined behavior. + */ + void setPairingController(BluetoothDetailsProfilesController profileController) { + if (isPairingControllerSet()) { + throw new IllegalStateException("The controller can only be set once. " + + "Forcibly replacing it will lead to undefined behavior"); + } + mProfileController = profileController; + } + + /** + * Checks whether mPairingController is set + * @return True when mPairingController is set, False otherwise + */ + boolean isPairingControllerSet() { + return mProfileController != null; + } + + private void onPositiveButtonClicked() { + mProfileController.onDialogPositiveClick(); + } + + private void onNegativeButtonClicked() { + mProfileController.onDialogNegativeClick(); + } +} diff --git a/src/com/android/settings/bluetooth/GroupBluetoothSettingsPreference.java b/src/com/android/settings/bluetooth/GroupBluetoothSettingsPreference.java new file mode 100644 index 0000000000..f55d6cfe69 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupBluetoothSettingsPreference.java @@ -0,0 +1,166 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; + +import com.android.settings.bluetooth.BluetoothDevicePreference.SortType; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.UserManager; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Pair; +import android.util.TypedValue; +import android.view.View; +import android.widget.ImageView; + +import com.android.settings.R; + + +public class GroupBluetoothSettingsPreference extends GearPreference { + + private static final String TAG = "GroupBluetoothSettingsPreference"; + + private static int sDimAlpha = Integer.MIN_VALUE; + + private int mGroupId = -1; + + private final UserManager mUserManager; + + private String contentDescription = null; + private boolean mHideSecondTarget = false; + private Resources mResources; + private int mVisibleCount = 0; + + public GroupBluetoothSettingsPreference(Context context, int groupId) { + super(context, null); + mGroupId = groupId; + mResources = getContext().getResources(); + mUserManager = context.getSystemService(UserManager.class); + if (sDimAlpha == Integer.MIN_VALUE) { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); + sDimAlpha = (int) (outValue.getFloat() * 255); + } + mVisibleCount = 0; + onDeviceAttributesChanged(); + } + + @Override + protected boolean shouldHideSecondTarget() { + return mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH) + || mHideSecondTarget; + } + + public void hideSecondTarget(boolean hideSecondTarget) { + mHideSecondTarget = hideSecondTarget; + } + + public void onDeviceAttributesChanged() { + /* + * The preference framework takes care of making sure the value has + * changed before proceeding. It will also call notifyChanged() if + * any preference info has changed from the previous value. + */ + Context context = getContext(); + String title = context.getString(R.string.group_settings); + setTitle(title + " " + (mGroupId + GroupUtils.GROUP_START_VAL)); + + // Used to gray out the item + setEnabled(true); // Change dynamically if required + + setVisible(true); // Change to dynamic if required + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + // Disable this view if the bluetooth enable/disable preference view is off + if (null != findPreferenceInHierarchy("bt_checkbox")) { + setDependency("bt_checkbox"); + } + + ImageView deviceDetails = (ImageView) view.findViewById(R.id.settings_button); + + if (deviceDetails != null) { + deviceDetails.setOnClickListener(this); + } + final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon); + if (imageView != null) { + imageView.setContentDescription(contentDescription); + // Set property to prevent Talkback from reading out. + imageView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + imageView.setVisibility(ImageView.VISIBLE); + } + super.onBindViewHolder(view); + } + + + @Override + protected int getSecondTargetResId() { + return R.layout.preference_widget_gear; + } + + public int getGroupId() { + return mGroupId; + } + + void onClicked() { + } + + public int getVisibleCount() { + return mVisibleCount; + } + + public int incrementChildCound() { + return ++mVisibleCount; + } + + public int decrementChildCount() { + return --mVisibleCount; + } + + public boolean isRemovePref() { + if (mVisibleCount == 0) { + return true; + } else { + return false; + } + } +} diff --git a/src/com/android/settings/bluetooth/GroupConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/GroupConnectedBluetoothDeviceUpdater.java new file mode 100644 index 0000000000..d13946153c --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupConnectedBluetoothDeviceUpdater.java @@ -0,0 +1,154 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.media.AudioManager; +import android.util.Log; + +import androidx.preference.Preference; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; + +/** + * Controller to maintain connected devices based on group + */ +public class GroupConnectedBluetoothDeviceUpdater extends GroupBluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { + + private static final String TAG = "GroupConnectedBluetoothDeviceUpdater"; + private static final String PREF_KEY = "connected_group_bt"; + + public GroupConnectedBluetoothDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(context, fragment, devicePreferenceCallback); + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + boolean isFilterMatched = isDeviceConnected(cachedDevice) && isGroupDevice(cachedDevice); + if (DBG) { + Log.d(TAG, "isFilterMatched cachedDevice " + cachedDevice.getName() + + " addr " + cachedDevice.getAddress() + " isConnected " + + isDeviceConnected(cachedDevice) + " isFilterMatched " + isFilterMatched); + } + return isFilterMatched; + } + + /** + * Force to update the list of bluetooth devices + */ + public void forceUpdate() { + if (mLocalManager == null) { + Log.e(TAG, "forceUpdate() Bluetooth is not supported on this device"); + return; + } + final Collection<CachedBluetoothDevice> cachedDevices = + mLocalManager.getCachedDeviceManager().getCachedDevicesCopy(); + final CachedBluetoothDeviceManager cachedManager = + mLocalManager.getCachedDeviceManager(); + if (BluetoothAdapter.getDefaultAdapter().isEnabled()) { + for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { + update(cachedBluetoothDevice); + } + } else { + removeAllDevicesFromPreference(); + removePreferenceIfNecessary(cachedDevices, cachedManager); + } + } + + private void removePreferenceIfNecessary(Collection<CachedBluetoothDevice> bluetoothDevices, + CachedBluetoothDeviceManager cachedManager) { + for (BluetoothDevice device : new ArrayList<>(mPreferenceMap.keySet())) { + if (!bluetoothDevices.contains(device)) { + final CachedBluetoothDevice cachedDevice = cachedManager.findDevice(device); + if (cachedDevice != null) { + removePreference(cachedDevice); + } else if(cachedDevice == null) { + try { + BluetoothDevicePreference pref = (BluetoothDevicePreference) + mPreferenceMap.get(device); + final CachedBluetoothDevice cacDev = pref.getBluetoothDevice(); + if (cacDev != null) { + removePreference(cacDev); + } + } catch (Exception e) { + Log.w(TAG, " removePreferenceIfNecessary " + e); + e.printStackTrace(); + } + } + } + } + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } + + @Override + protected void addPreference(CachedBluetoothDevice cachedDevice, + @BluetoothDevicePreference.SortType int type) { + final BluetoothDevice device = cachedDevice.getDevice(); + if (!mPreferenceMap.containsKey(device)) { + final BluetoothDevicePreference btPreference = + new BluetoothDevicePreference(mPrefContext, cachedDevice, + true /* showDeviceWithoutNames */, + type, true /*hide summary */); + btPreference.setOnGearClickListener(mDeviceProfilesListener); + if (this instanceof Preference.OnPreferenceClickListener) { + btPreference.setOnPreferenceClickListener( + (Preference.OnPreferenceClickListener)this); + } + mPreferenceMap.put(device, btPreference); + mDevicePreferenceCallback.onDeviceAdded(btPreference); + } + } + + @Override + public boolean onPreferenceClick(Preference preference) { + if (DBG) { + Log.d(TAG, " onPreferenceClick " + preference); + } + mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory()); + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + return device.setActive(); + } +} diff --git a/src/com/android/settings/bluetooth/GroupForgetDialogFragment.java b/src/com/android/settings/bluetooth/GroupForgetDialogFragment.java new file mode 100644 index 0000000000..da9c7cd3fc --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupForgetDialogFragment.java @@ -0,0 +1,103 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.bluetooth; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.Log; +import androidx.appcompat.app.AlertDialog; + +import java.util.List; + +import com.android.settings.R; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.instrumentation.Instrumentable; + +/** Implements an AlertDialog for confirming that a user wishes to unpair or "forget" +* a Group of devices*/ +public class GroupForgetDialogFragment extends InstrumentedDialogFragment { + + private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP; + protected static final String TAG = "GroupForgetDialogFragment"; + private static final String KEY_GROUPID = "groupid"; + private static List<CachedBluetoothDevice> mDevices ; + private int mGroupId; + private GroupUtils mGroupUtils; + public static GroupForgetDialogFragment newInstance(int groupId) { + Bundle args = new Bundle(1); + args.putInt(KEY_GROUPID, groupId); + GroupForgetDialogFragment dialog = new GroupForgetDialogFragment(); + dialog.setArguments(args); + return dialog; + } + + String getGroupTitle() { + mGroupId = getArguments().getInt(KEY_GROUPID); + return mGroupUtils.getGroupTitle(mGroupId); + } + + @Override + public int getMetricsCategory() { + return Instrumentable.METRICS_CATEGORY_UNKNOWN; + } + + @Override + public Dialog onCreateDialog(Bundle inState) { + DialogInterface.OnClickListener onConfirm = (dialog, which) -> { + forget(); + Activity activity = getActivity(); + if (activity != null) { + activity.finish(); + } + }; + Context context = getContext(); + mGroupUtils = new GroupUtils(context); + AlertDialog dialog = new AlertDialog.Builder(context) + .setPositiveButton(R.string.groupaudio_unpair_dialog_forget_confirm_button, + onConfirm) + .setNegativeButton(android.R.string.cancel, null) + .create(); + dialog.setTitle(R.string.groupaudio_unpair_dialog_title); + dialog.setMessage(context.getString(R.string.groupaudio_unpair_dialog_body, + getGroupTitle())); + return dialog; + } + + private void forget() { + mGroupUtils.forgetGroup(mGroupId); + } +} diff --git a/src/com/android/settings/bluetooth/GroupSavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/GroupSavedBluetoothDeviceUpdater.java new file mode 100644 index 0000000000..621675fb09 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupSavedBluetoothDeviceUpdater.java @@ -0,0 +1,143 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import androidx.preference.Preference; + +import com.android.settings.R; + +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; + +/** + * Maintain and update saved Group devices(bonded but not connected) + */ +public class GroupSavedBluetoothDeviceUpdater extends GroupBluetoothDeviceUpdater + implements Preference.OnPreferenceClickListener { + + private static final String TAG = "GroupSavedBluetoothDeviceUpdater"; + private static final String PREF_KEY = "saved_group_bt"; + private BluetoothAdapter mBluetoothAdapter; + + public GroupSavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(context, fragment, devicePreferenceCallback); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } + + @Override + public void forceUpdate() { + if (mBluetoothAdapter.isEnabled()) { + final CachedBluetoothDeviceManager cachedManager = + mLocalManager.getCachedDeviceManager(); + final List<BluetoothDevice> bluetoothDevices = + mBluetoothAdapter.getMostRecentlyConnectedDevices(); + removePreferenceIfNecessary(bluetoothDevices, cachedManager); + for(BluetoothDevice device : bluetoothDevices) { + final CachedBluetoothDevice cachedDevice = cachedManager.findDevice(device); + if (cachedDevice != null) { + update(cachedDevice); + } + } + } else { + removeAllDevicesFromPreference(); + } + } + + private void removePreferenceIfNecessary(List<BluetoothDevice> bluetoothDevices, + CachedBluetoothDeviceManager cachedManager) { + for (BluetoothDevice device : new ArrayList<>(mPreferenceMap.keySet())) { + if (!bluetoothDevices.contains(device)) { + final CachedBluetoothDevice cachedDevice = cachedManager.findDevice(device); + if (cachedDevice != null) { + removePreference(cachedDevice); + } else if(cachedDevice == null) { + try { + BluetoothDevicePreference pref = (BluetoothDevicePreference) + mPreferenceMap.get(device); + final CachedBluetoothDevice cacDev = pref.getBluetoothDevice(); + if (cacDev != null) { + removePreference(cacDev); + } + } catch (Exception e) { + Log.w(TAG, "removePreferenceIfNecessary " + e); + e.printStackTrace(); + } + } + } + } + } + + @Override + public void update(CachedBluetoothDevice cachedDevice) { + if (isFilterMatched(cachedDevice)) { + addPreference(cachedDevice, BluetoothDevicePreference.SortType.TYPE_NO_SORT); + } else { + removePreference(cachedDevice); + } + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + if (DBG) { + Log.d(TAG, " cachedDevice : " + cachedDevice + ", isConnected " + device.isConnected() + +" isBonded " + (device.getBondState() == BluetoothDevice.BOND_BONDED)); + } + return (device.getBondState() == BluetoothDevice.BOND_BONDED) + && !device.isConnected() && isGroupDevice(cachedDevice); + } + + @Override + public boolean onPreferenceClick(Preference preference) { + mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory()); + final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference) + .getBluetoothDevice(); + device.connect(); + return true; + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } +} diff --git a/src/com/android/settings/bluetooth/GroupUtils.java b/src/com/android/settings/bluetooth/GroupUtils.java new file mode 100644 index 0000000000..04d1a85901 --- /dev/null +++ b/src/com/android/settings/bluetooth/GroupUtils.java @@ -0,0 +1,669 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.bluetooth; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settings.widget.GroupPreferenceCategory; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.DeviceGroupClientProfile; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; +import com.android.settings.R; + +import com.android.settings.core.SubSettingLauncher; +import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.DeviceGroup; +import android.content.Context; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; +import android.os.Bundle; + +/** + * GroupUtils is a helper class that contains various Group UI specific + * methods for add preference , remove preference, and group actions + */ +public class GroupUtils { + + public static final String TAG_GROUP = "Group"; + private static final String TAG = "GroupUtilss"; + private static final boolean D = ConnectedDeviceDashboardFragment.DBG_GROUP; + private static final boolean V = Log.isLoggable(TAG, Log.VERBOSE); + private Context mCtx; + private CachedBluetoothDeviceManager mCacheDeviceNamanger; + private DeviceGroupClientProfile mGroupClientProfile; + private DeviceGroup mDeviceGroup; + private static final int INVALID_SIZE = 0; + private static final int INVALID_GROUPID = -1; + public static final int GROUP_START_VAL = 1; + private static final String PROPERTY_GROUP = "persist.vendor.service.bt.adv_audio_mask"; + private boolean mIsGroupEnabled = false; + + protected LocalBluetoothProfileManager mProfileManager; + private LocalBluetoothManager mLocalBluetoothManager; + private LocalBluetoothProfile mBCProfile = null; + private static final String KEY_DEVICE_ADDRESS = "device_address"; + private static final String KEY_GROUP_OP = "group_op"; + /* + * Returns whether if the device is group device. + */ + boolean isGroupDevice(CachedBluetoothDevice cachedDevice) { + if (!mIsGroupEnabled) { + if (D) { + loge(" GroupProfile not enabled"); + } + return false; + } + if (!cachedDevice.isGroupDevice()) { + if (cachedDevice.isTypeUnKnown()) { + int type = cachedDevice.getDevice().getDeviceType(); + updateGroupStatus(cachedDevice, type); + } + } + if (D) { + Log.d(TAG, "isGroupDevice " + cachedDevice.isGroupDevice() + cachedDevice + + " name " + cachedDevice.getName() + " type " + cachedDevice.getmType()); + } + return cachedDevice.isGroupDevice(); + } + + String getGroupTitle(int groupId) { + return " " + (groupId + GROUP_START_VAL); + } + + /* + * Get group id associated with this device + */ + int getGroupId(CachedBluetoothDevice device) { + int groupId = device.getGroupId(); + if (groupId == -1) { + loge(" groupId is -1"); + } + if (D) { + Log.d(TAG, "getgroupId " + groupId + " device " + device); + } + return groupId; + } + + private void updateGroupStatus(CachedBluetoothDevice device, int groupId) { + device.setDeviceType(groupId); + CachedBluetoothDevice cacheDevice = mCacheDeviceNamanger.findDevice(device.getDevice()); + if (cacheDevice != null) { + cacheDevice.setDeviceType(groupId); + if (D) { + Log.d(TAG, "updateGroupStatus updated " + device + " " + groupId); + } + } else { + loge("updateGroupStatus failed " + device + " groupId " + groupId); + } + } + + public GroupUtils(Context ctx) { + mCtx = ctx; + mCacheDeviceNamanger = Utils.getLocalBtManager(mCtx).getCachedDeviceManager(); + isGroupEnabled(); + mLocalBluetoothManager = Utils.getLocalBtManager(mCtx); + if (mLocalBluetoothManager == null) { + Log.e(TAG, "Bluetooth is not supported on this device"); + return; + } + mProfileManager = mLocalBluetoothManager.getProfileManager(); + mBCProfile = mProfileManager.getBCProfile(); + } + + private int getGroupId(Preference preference) { + CachedBluetoothDevice dev = null; + int groupId = -1; + if (preference instanceof BluetoothDevicePreference) { + dev = ((BluetoothDevicePreference) preference).getBluetoothDevice(); + groupId = getGroupId(dev); + } + if (groupId == -1) { + loge("group id not found " + dev.getAddress()); + } + return groupId; + } + + private boolean isNewGroup(int id, ArrayList< GroupPreferenceCategory> groupList) { + boolean isNew = true; + for (int i = 0; i< groupList.size() - 1; i++) { + GroupPreferenceCategory tempGroup = groupList.get(i); + if (tempGroup == null) { + loge("isNewGroup tempGroup null"); + return false; + } + int val = tempGroup.getGroupId(); + if (D) { + Log.d(TAG, "isNewGroup val " + val + " key " + tempGroup.getKey()); + } + if (id == val) { + isNew = false; + break; + } + } + if (D) { + Log.d(TAG, "isNewGroup id " + id + " val " + isNew); + } + return isNew; + } + + private boolean isAllFilled(int id, ArrayList< GroupPreferenceCategory> groupList) { + boolean filled = true; + for (int i = 0; i< groupList.size() - 1; i++) { + GroupPreferenceCategory tempGroup = groupList.get(i); + if (tempGroup == null) { + loge("isAllFilled"); + return false; + } + int val = tempGroup.getGroupId(); + if (id == val) { + filled = false; + break; + } + } + return filled; + } + + + private GroupPreferenceCategory getParentGroup(ArrayList< GroupPreferenceCategory> groupList, + Preference preference) { + GroupPreferenceCategory group = null; + for (int i = 0; i< groupList.size() - 1; i++) { + GroupPreferenceCategory tempGroup = groupList.get(i); + if (tempGroup.getPreferenceCount() == 0) { + group = tempGroup; + break; + } + } + return group; + } + + private GroupPreferenceCategory getExistingGroup(ArrayList< GroupPreferenceCategory> mGroupList, + Preference preference) { + GroupPreferenceCategory group = null; + int groupId = getGroupId(preference); + for (GroupPreferenceCategory tempGroup : mGroupList) { + int val = tempGroup.getGroupId(); + if (groupId == val) { + group = tempGroup; + break; + } + } + return group; + } + + private GroupBluetoothSettingsPreference getHedaer(int groupId, + OnGearClickListener listener ) { + GroupBluetoothSettingsPreference headerPreference = + new GroupBluetoothSettingsPreference(mCtx, groupId); + headerPreference.setOnGearClickListener(listener); + return headerPreference; + } + + /* + * Add preference based on below conditions + * a) Get group id based on Preference + * b) Check if already group header is present or not + * c) If new group create header + * d) Get available GroupPreferenceCategory instance + * e) Add Header and preference + * f) If group already present get group based on id + * g) Add preference to existing header + */ + public void addPreference(ArrayList< GroupPreferenceCategory> listCategories, + Preference preference, OnGearClickListener listener) { + int groupId = getGroupId(preference); + if (groupId == -1) { + loge("addPreference groupId is not valid "+ groupId); + return; + } + boolean isNewGroup = isNewGroup(groupId, listCategories); + if (D) { + Log.d(TAG, "addPreference " + preference + " isNewGroup " + isNewGroup); + } + String key ; + GroupPreferenceCategory group = null; + if (isNewGroup) { + GroupBluetoothSettingsPreference header = getHedaer(groupId, listener); + group = getParentGroup(listCategories, preference); + if (group == null) { + loge("getParentGroup not found for groupId " + groupId); + isAllGroupsFilled(listCategories, header); + return; + } + group.setGroupId(groupId); + group.addPreference(header); + group.addPreference(preference); + group.setVisible(true); + } else { + group = getExistingGroup(listCategories, preference); + if (group == null) { + loge("getExistingGroup not found for groupId " + groupId); + return; + } + group.addPreference(preference); + } + if (D) { + Log.d(TAG , "addPreference key " + group.getKey()); + } + } + + /* + * Remove preference based on below conditions + * a) Get existing Preference Category + * b) Remove preference from category + * c) If preference count is one remove header + */ + public void removePreference(ArrayList< GroupPreferenceCategory> listCategories, + Preference preference) { + GroupPreferenceCategory group = getExistingGroup(listCategories, preference); + if (group == null) { + loge("removePreference group null "); + removePreference(listCategories.get(listCategories.size()-1), preference); + return; + } + group.removePreference(preference); + if (group.getPreferenceCount() == 1) { + group.setGroupId(INVALID_GROUPID); + group.removeAll(); + group.setVisible(false); + } + } + + /* + * Remove Header if child count is 0 + */ + private void removePreference(GroupPreferenceCategory groupCategory, + Preference preference) { + int size = groupCategory.getPreferenceCount(); + if (size == 0) { + loge("removePreference Header invalid"); + return; + } + int groupId = getGroupId(preference); + if (groupId == INVALID_GROUPID) { + loge("removePreference Header groupId is invalid"); + return; + } + for (int i=0;i<size; i++) { + GroupBluetoothSettingsPreference headerPreference = + (GroupBluetoothSettingsPreference) groupCategory.getPreference(i); + if (D) { + Log.d(TAG, "removePreference Header headerPreference " + + headerPreference + " header id " + + headerPreference.getGroupId() + " groupId " + groupId ); + } + if (headerPreference.getGroupId() == groupId) { + int chCount = headerPreference.decrementChildCount(); + if (D) { + Log.d(TAG,"removePreference Header group id chCount " + chCount ); + } + if (chCount <= 0) { + groupCategory.removePreference(headerPreference); + } + } + } + } + + /* + * If more than nine groups add headers only + */ + private void isAllGroupsFilled(ArrayList<GroupPreferenceCategory> listCategories, + GroupBluetoothSettingsPreference preference) { + boolean isFilled = isAllFilled(-1, listCategories); + GroupPreferenceCategory group = listCategories.get(listCategories.size() - 1); + if (isFilled) { + if (group == null) { + loge("isAllGroupsFilled received invalid group"); + return; + } + if (!group.getKey().contains("remaining")) { + loge("isAllGroupsFilled not last group"); + return; + } + int size = group.getPreferenceCount(); + if (D) { + Log.d(TAG, "isAllGroupsFilled size " + size); + } + boolean found = false; + for (int i = 0; i < size; i++) { + if (group.getPreference(i) instanceof GroupBluetoothSettingsPreference) { + GroupBluetoothSettingsPreference pref = + (GroupBluetoothSettingsPreference )group.getPreference(i); + if (preference.getGroupId() == pref.getGroupId()) { + found = true; + int chCount = pref.incrementChildCound(); + if(D) { + Log.d(TAG, "isAllGroupsFilled updated chCount " + chCount); + } + break; + } + } + } + if (!found) { + int chCount = preference.incrementChildCound(); + group.addPreference(preference); + if (D) { + Log.d(TAG, "isAllGroupsFilled added chCount " + chCount); + } + } + } + } + + ArrayList<CachedBluetoothDevice> getCahcedDevice(int groupId) { + Collection<CachedBluetoothDevice> cachedDevices = + mCacheDeviceNamanger.getCachedDevicesCopy(); + ArrayList<CachedBluetoothDevice> list = new ArrayList<CachedBluetoothDevice>(); + for (CachedBluetoothDevice cachedDevice : cachedDevices) { + if (cachedDevice != null && isGroupDeviceBonded(cachedDevice) + && getGroupId(cachedDevice) == groupId) { + list.add(cachedDevice); + } + } + if (D) { + Log.d(TAG, "getCahcedDevice " + groupId + " list " + list + " " + list.size()); + } + return list; + } + + public BluetoothDevice getAnyBCConnectedDevice (int groupId) { + BluetoothDevice bcMemberDevice = null; + + mDeviceGroup = mGroupClientProfile.getGroup(groupId); + if (mDeviceGroup == null) { + Log.e(TAG, "getAnyBCConnectedDevice: dGrp is null"); + return null; + } + if (mBCProfile == null) { + Log.e(TAG, "getAnyBCConnectedDevice: BCProfile is null"); + return null; + } + List<BluetoothDevice> setMembers = mDeviceGroup.getDeviceGroupMembers(); + for (BluetoothDevice dev : setMembers) { + if (mBCProfile.getConnectionStatus(dev) == BluetoothProfile.STATE_CONNECTED) { + bcMemberDevice = dev; + break; + } + } + return bcMemberDevice; + } + void launchAddSourceGroup(int groupId) { + Class<?> SADetail = null; + try { + SADetail = Class.forName("com.android.settings.bluetooth.BluetoothSADetail"); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no SADetail exists"); + SADetail = null; + } + if (SADetail != null) { + BluetoothDevice bcMemberDevice = getAnyBCConnectedDevice(groupId); + final Bundle args = new Bundle(); + if (bcMemberDevice == null) { + //do nothing + return; + } + args.putString(KEY_DEVICE_ADDRESS, + bcMemberDevice.getAddress()); + args.putShort(KEY_GROUP_OP, + (short)1); + + new SubSettingLauncher(mCtx) + .setDestination("com.android.settings.bluetooth.BluetoothSADetail") + .setArguments(args) + .setTitleRes(R.string.bluetooth_search_broadcasters) + .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_DEVICE_PICKER) + .launch(); + } + return; + } + + boolean connectGroup(int groupId) { + if (isValid()) { + return mGroupClientProfile.connectGroup(groupId); + } + return false; + } + + boolean disconnectGroup(int groupId) { + if (isValid()) { + return mGroupClientProfile.disconnectGroup(groupId); + } + return false; + } + + boolean forgetGroup(int groupId) { + if (isValid()) { + return mGroupClientProfile.forgetGroup(groupId); + } + return false; + } + + boolean isGroupDiscoveryInProgress(int groupId) { + if (!isValid()) { + return false; + } + return mGroupClientProfile.isGroupDiscoveryInProgress(groupId); + } + + boolean startGroupDiscovery(int groupId) { + if (!isValid()) { + return false; + } + boolean isDiscovering= mGroupClientProfile.isGroupDiscoveryInProgress(groupId); + if (D) { + Log.d(TAG, "startGroupDiscovery " + groupId + "isDiscovering " + isDiscovering); + } + if (!isDiscovering) { + mGroupClientProfile.startGroupDiscovery(groupId); + return true; + } + return false; + } + + boolean stopGroupDiscovery(int groupId) { + if (!isValid()) { + return false; + } + boolean isDiscovering = mGroupClientProfile.isGroupDiscoveryInProgress(groupId); + if (D) { + Log.d(TAG, "stopGroupDiscovery " + groupId + "isDiscovering " + isDiscovering); + } + if (isDiscovering) { + mGroupClientProfile.stopGroupDiscovery(groupId); + return true; + } + return false; + } + + int getGroupSize(int groupId) { + int size = INVALID_SIZE; + if (isValid()) { + mDeviceGroup = mGroupClientProfile.getGroup(groupId); + if (mDeviceGroup != null) { + size = mDeviceGroup.getDeviceGroupSize(); + } + } + if (D) { + Log.d(TAG, "getDeviceGroupSize size " + size); + } + return size; + } + + public boolean isHidePCGGroups() { + boolean isHide = true; + List<BluetoothDevice> bluetoothDevices = + BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices(); + if (bluetoothDevices != null && bluetoothDevices.size() > 0) { + for (BluetoothDevice device : bluetoothDevices) { + CachedBluetoothDevice cachedDevice = mCacheDeviceNamanger.findDevice(device); + if (cachedDevice != null && isGroupDeviceBondedOnly(cachedDevice)) { + isHide = false; + break; + } + } + } + if (D) { + Log.d(TAG, "isHidePCGGroups " + isHide); + } + return isHide; + } + + boolean isHideGroupOptions(int groupId) { + boolean isHide = true; + Collection<CachedBluetoothDevice> cachedDevices = + mCacheDeviceNamanger.getCachedDevicesCopy(); + if (cachedDevices != null && cachedDevices.size() > 0) { + for (CachedBluetoothDevice cachedDevice : cachedDevices) { + if (cachedDevice != null && isGroupDeviceBonded(cachedDevice) + && isGroupIdMatch(cachedDevice, groupId)) { + isHide = false; + break; + } + } + } + if (D) { + Log.d(TAG, "isHideGroupOptions " + isHide); + } + return isHide; + } + + private boolean isGroupDeviceBondedOnly(CachedBluetoothDevice cachedDevice) { + if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED + && !cachedDevice.isConnected() && isGroupDevice(cachedDevice)) { + return true; + } else { + return false; + } + } + + private boolean isGroupIdMatch(CachedBluetoothDevice cachedDevice, int groupId) { + return groupId == getGroupId(cachedDevice); + } + + private boolean isGroupDeviceBonded(CachedBluetoothDevice cachedDevice) { + if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED + && isGroupDevice(cachedDevice)) { + return true; + } else { + return false; + } + } + + private void loge(String msg) { + Log.e(TAG, msg); + } + + private boolean isValid() { + if (mGroupClientProfile == null) { + LocalBluetoothProfileManager profilemanager = + Utils.getLocalBtManager(mCtx).getProfileManager(); + mGroupClientProfile = profilemanager.getDeviceGroupClientProfile(); + } + return (mGroupClientProfile == null) ? false : true; + } + + private void isGroupEnabled() { + try { + int advAudioFeatureMask = SystemProperties.getInt(PROPERTY_GROUP, 0); + if (D) { + Log.d(TAG,"isGroupEnabled advAudioFeatureMask " + advAudioFeatureMask); + } + if (advAudioFeatureMask != 0) { + mIsGroupEnabled = true; + } + } catch (Exception e) { + mIsGroupEnabled = false; + Log.e(TAG, "isGroupEnabled " + e); + } + } + + boolean isUpdate(int groupId, CachedBluetoothDevice cachedDevice) { + return (isGroupDevice(cachedDevice) && groupId == getGroupId(cachedDevice)); + } + + boolean addDevice(ArrayList<CachedBluetoothDevice> deviceList, int groupId, + CachedBluetoothDevice cachedDevice) { + boolean add = true; + boolean isAdded = false; + if (isUpdate(groupId, cachedDevice)) { + for (CachedBluetoothDevice device : deviceList) { + if (device.getAddress().equals(cachedDevice.getAddress())) { + add = false; + break; + } + } + if (add) { + isAdded = deviceList.add(cachedDevice); + } + } + if (D) { + Log.d(TAG, "addDevice cachedDevice " + cachedDevice + " name " + + cachedDevice.getName() + " is added " + isAdded); + } + return add; + } + + boolean removeDevice(ArrayList<CachedBluetoothDevice> deviceList, int groupId, + CachedBluetoothDevice cachedDevice) { + boolean remove = false; + boolean isremoved = false; + CachedBluetoothDevice removedDevice = null ; + if (isUpdate(groupId, cachedDevice)) { + for (CachedBluetoothDevice device : deviceList) { + if (device.getAddress().equals(cachedDevice.getAddress())) { + remove = true; + removedDevice = device; + break; + } + } + if (remove) { + isremoved = deviceList.remove(removedDevice); + } + } + if (D) { + Log.d(TAG, "removeDevice cachedDevice " + cachedDevice + " name " + + cachedDevice.getName() + " isremoved " + isremoved); + } + return remove; + } +} diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java index dab4f231e3..a4e7e334f9 100644 --- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java @@ -1,4 +1,37 @@ /* + *Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -102,10 +135,13 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater if (DBG) { Log.d(TAG, "isFilterMatched() device name : " + cachedDevice.getName() + ", is connected : " + device.isConnected() + ", is profile connected : " - + cachedDevice.isConnected()); + + cachedDevice.isConnected() + + ", is twsplusdevice : " + device.isTwsPlusDevice()); } return device.getBondState() == BluetoothDevice.BOND_BONDED - && (mDisplayConnected || !device.isConnected()); + && (mDisplayConnected || !device.isConnected()) + && !device.isTwsPlusDevice() && !isGroupDevice(cachedDevice) + && !isPrivateAddr(cachedDevice); } @Override diff --git a/src/com/android/settings/bluetooth/SavedBluetoothTwsDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothTwsDeviceUpdater.java new file mode 100644 index 0000000000..eae4c5b56b --- /dev/null +++ b/src/com/android/settings/bluetooth/SavedBluetoothTwsDeviceUpdater.java @@ -0,0 +1,91 @@ +/* + *Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + * Copyright (C) 2017 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.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; + +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; + +/** + * Maintain and update saved TWS+ bluetooth devices(bonded but not connected) + */ +public class SavedBluetoothTwsDeviceUpdater extends BluetoothDeviceUpdater { + + private static final String PREF_KEY = "saved_bt_tws"; + + public SavedBluetoothTwsDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(context, fragment, devicePreferenceCallback); + } + + SavedBluetoothTwsDeviceUpdater(Context context, DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback, + LocalBluetoothManager localBluetoothManager) { + super(context, fragment, devicePreferenceCallback, localBluetoothManager); + } + + @Override + public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + return device.getBondState() == BluetoothDevice.BOND_BONDED && + !device.isConnected() && device.isTwsPlusDevice() && !isGroupDevice(cachedDevice) + && !isPrivateAddr(cachedDevice); + } + + @Override + protected String getPreferenceKey() { + return PREF_KEY; + } +} diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java index 4591b7f216..9e86fa62ec 100644 --- a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java @@ -19,6 +19,11 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import android.os.SystemProperties; +import android.util.Log; +import android.view.View; +import androidx.fragment.app.Fragment; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController; @@ -28,8 +33,13 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.MainSwitchBarController; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.widget.FooterPreference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; /** * Dedicated screen for allowing the user to toggle bluetooth which displays relevant information to @@ -40,6 +50,12 @@ public class BluetoothDashboardFragment extends DashboardFragment { private static final String TAG = "BluetoothDashboardFrag"; private static final String KEY_BLUETOOTH_SCREEN_FOOTER = "bluetooth_screen_footer"; + private static final String BLUETOOTH_ADV_AUDIO_MASK_PROP + = "persist.vendor.service.bt.adv_audio_mask"; + private static final String BLUETOOTH_BROADCAST_UI_PROP = "persist.bluetooth.broadcast_ui"; + private static final int BROADCAST_MASK = 0x04; + private static boolean mBroadcastEnabled = false; + private static boolean mBroadcastPropertyChecked = false; private FooterPreference mFooterPreference; private SettingsMainSwitchBar mSwitchBar; @@ -78,6 +94,59 @@ public class BluetoothDashboardFragment extends DashboardFragment { } @Override + protected void displayResourceTilesToScreen(PreferenceScreen screen) { + if (mBroadcastEnabled == false) { + screen.removePreference(screen.findPreference("bluetooth_screen_broadcast_enable")); + screen.removePreference( + screen.findPreference("bluetooth_screen_broadcast_pin_configure")); + } + super.displayResourceTilesToScreen(screen); + } + + @Override + protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { + List<AbstractPreferenceController> controllers = new ArrayList<>(); + if (mBroadcastPropertyChecked == false) { + int advAudioMask = SystemProperties.getInt(BLUETOOTH_ADV_AUDIO_MASK_PROP, 0); + mBroadcastEnabled = (((advAudioMask & BROADCAST_MASK) == BROADCAST_MASK) && + SystemProperties.getBoolean(BLUETOOTH_BROADCAST_UI_PROP, true)); + mBroadcastPropertyChecked = true; + } + + if (mBroadcastEnabled == false) { + return controllers; + } + + Log.d (TAG, "createPreferenceControllers for Broadcast"); + + try { + Class<?> classBroadcastPinController = + Class.forName("com.android.settings.bluetooth.BluetoothBroadcastPinController"); + Class<?> classBroadcastEnableController = + Class.forName("com.android.settings.bluetooth.BluetoothBroadcastEnableController"); + Constructor ctorPin, ctorEnable; + ctorPin = classBroadcastPinController + .getDeclaredConstructor(new Class[] {Context.class, Lifecycle.class}); + ctorEnable = classBroadcastEnableController + .getDeclaredConstructor(new Class[] {Context.class, String.class, Lifecycle.class}); + Object objBroadcastPinController = ctorPin.newInstance(context, getSettingsLifecycle()); + Object objBroadcastEnableController = ctorEnable + .newInstance(context, new String("bluetooth_screen_broadcast_enable"), getSettingsLifecycle()); + objBroadcastPinController.getClass().getMethod("setFragment", Fragment.class) + .invoke(objBroadcastPinController, (Fragment) this); + controllers.add((AbstractPreferenceController) objBroadcastPinController); + controllers.add((AbstractPreferenceController) objBroadcastEnableController); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InvocationTargetException | InstantiationException | IllegalArgumentException | + ExceptionInInitializerError e) { + mBroadcastEnabled = false; + e.printStackTrace(); + } finally { + return controllers; + } + } + + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java index c8eb488df0..3fcc136fab 100644 --- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java @@ -1,4 +1,37 @@ /* + *Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,10 +65,15 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.slices.SlicePreferenceController; import com.android.settingslib.search.SearchIndexable; +import android.util.Log; + @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) public class ConnectedDeviceDashboardFragment extends DashboardFragment { private static final String TAG = "ConnectedDeviceFrag"; + private static final String TAG_GROUP = "Group"; + + public static final boolean DBG_GROUP = Log.isLoggable(TAG_GROUP, Log.DEBUG); private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -77,7 +115,10 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment { } use(AvailableMediaDeviceGroupController.class).init(this); use(ConnectedDeviceGroupController.class).init(this); + use(SavedTwsDeviceGroupController.class).init(this); use(PreviouslyConnectedDevicePreferenceController.class).init(this); + use(GroupConnectedBluetoothDevicesController.class).init(this); + use(GroupPreviouslyConnectedDevicePreferenceController.class).init(this); use(SlicePreferenceController.class).setSliceUri(nearbyEnabled ? Uri.parse(getString(R.string.config_nearby_devices_slice_uri)) : null); diff --git a/src/com/android/settings/connecteddevice/GroupConnectedBluetoothDevicesController.java b/src/com/android/settings/connecteddevice/GroupConnectedBluetoothDevicesController.java new file mode 100644 index 0000000000..8ef95f0b88 --- /dev/null +++ b/src/com/android/settings/connecteddevice/GroupConnectedBluetoothDevicesController.java @@ -0,0 +1,174 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.connecteddevice; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.util.Log; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupBluetoothSettingsPreference; +import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupConnectedBluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupUtils; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settings.widget.GroupPreferenceCategory; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import java.util.ArrayList; + +import com.android.settings.R; + +/** + * Controller to maintain for all connected Group devices. + * It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} + */ +public class GroupConnectedBluetoothDevicesController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback, OnGearClickListener { + + private static final String TAG = "GroupConnectedBluetoothDevicesController"; + private static final String KEY_ONE = "group_one"; + private static final String KEY_TWO = "group_two"; + private static final String KEY_THREE = "group_three"; + private static final String KEY_FOUR = "group_four"; + private static final String KEY_FIVE = "group_five"; + private static final String KEY_SIX = "group_six"; + private static final String KEY_SEVEN = "group_seven"; + private static final String KEY_EIGHT = "group_eight"; + private static final String KEY_NINE = "group_nine"; + private static final String KEY_REMAINING = "group_remaining"; + private static final String KEY_GROUP = "group_connected_device_list"; + private GroupBluetoothSettingsPreference mGroupSettings; + private PreferenceGroup mPreferenceGroup; + private GroupConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater; + private Preference mPreferenceSeeAll; + private GroupPreferenceCategory mPreferenceGroupOne, mPreferenceGroupTwo, mPreferenceGroupThree, + mPreferenceGroupFour, mPreferenceGroupFive, mPreferenceGroupSix, mPreferenceGroupSeven, + mPreferenceGroupEight, mPreferenceGroupNine,mPreferenceGroupRemaining; + private ArrayList< GroupPreferenceCategory> mGroupList = + new ArrayList<GroupPreferenceCategory>(); + private GroupUtils mGroupUtils; + private Context mPerfCtx; + + public GroupConnectedBluetoothDevicesController(Context context) { + super(context, KEY_ONE); + } + + @Override + public void onStart() { + mBluetoothDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mBluetoothDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceGroup = screen.findPreference(KEY_GROUP); + mPreferenceGroupOne = screen.findPreference(KEY_ONE); + mPreferenceGroupTwo = screen.findPreference(KEY_TWO); + mPreferenceGroupThree = screen.findPreference(KEY_THREE); + mPreferenceGroupFour = screen.findPreference(KEY_FOUR); + mPreferenceGroupFive = screen.findPreference(KEY_FIVE); + mPreferenceGroupSix = screen.findPreference(KEY_SIX); + mPreferenceGroupSeven = screen.findPreference(KEY_SEVEN); + mPreferenceGroupEight= screen.findPreference(KEY_EIGHT); + mPreferenceGroupNine = screen.findPreference(KEY_NINE); + mPreferenceGroupRemaining = screen.findPreference(KEY_REMAINING); + mGroupList.add(mPreferenceGroupOne); + mGroupList.add(mPreferenceGroupTwo); + mGroupList.add(mPreferenceGroupThree); + mGroupList.add(mPreferenceGroupFour); + mGroupList.add(mPreferenceGroupFive); + mGroupList.add(mPreferenceGroupSix); + mGroupList.add(mPreferenceGroupSeven); + mGroupList.add(mPreferenceGroupEight); + mGroupList.add(mPreferenceGroupNine); + mGroupList.add(mPreferenceGroupRemaining); + + if (isAvailable()) { + final Context context = screen.getContext(); + mGroupUtils = new GroupUtils(context); + mBluetoothDeviceUpdater.setPrefContext(context); + mBluetoothDeviceUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + final PackageManager packageManager = mContext.getPackageManager(); + return (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) + ? AVAILABLE_UNSEARCHABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY_ONE; + } + + @Override + public void onDeviceAdded(Preference preference) { + mGroupUtils.addPreference(mGroupList, preference, this); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mGroupUtils.removePreference(mGroupList, preference); + } + + public void init(DashboardFragment fragment) { + mBluetoothDeviceUpdater = new GroupConnectedBluetoothDeviceUpdater(fragment.getContext(), + fragment, GroupConnectedBluetoothDevicesController.this); + } + + @Override + public void onGearClick(GearPreference p) { + GroupBluetoothSettingsPreference pre =(GroupBluetoothSettingsPreference)p; + mBluetoothDeviceUpdater.launchgroupOptions(pre); + } +} diff --git a/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDeviceDashboardFragment.java new file mode 100644 index 0000000000..9c639c4580 --- /dev/null +++ b/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDeviceDashboardFragment.java @@ -0,0 +1,87 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.connecteddevice; + +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.bluetooth.GroupUtils; +import com.android.settings.dashboard.DashboardFragment; +import android.util.Log; +import com.android.settingslib.core.instrumentation.Instrumentable; + +import java.security.spec.MGF1ParameterSpec; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This fragment contains previously connected Group devices + */ +public class GroupPreviouslyConnectedDeviceDashboardFragment extends DashboardFragment { + + private static final String TAG = "GroupPreviouslyConnectedDeviceDashboardFragment"; + static final String KEY_PREVIOUSLY_CONNECTED_GROUP_DEVICES = "group_saved_device_list"; + private GroupUtils mGroupUtils; + + @Override + public int getHelpResource() { + return R.string.group_help_url_previously_connected_devices; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.previously_connected_group_devices; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public int getMetricsCategory() { + return Instrumentable.METRICS_CATEGORY_UNKNOWN; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mGroupUtils = new GroupUtils(context); + use(GroupSavedDeviceController.class).init(this); + } + + @Override + public void onResume() { + super.onResume(); + if (mGroupUtils.isHidePCGGroups()) { + finish(); + } + } +} diff --git a/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDevicePreferenceController.java new file mode 100644 index 0000000000..0b7d6706af --- /dev/null +++ b/src/com/android/settings/connecteddevice/GroupPreviouslyConnectedDevicePreferenceController.java @@ -0,0 +1,148 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.connecteddevice; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupSavedBluetoothDeviceUpdater; +import com.android.settings.bluetooth.Utils; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.overlay.FeatureFactory; + +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import android.util.Log; + +/** + * Controller to maintain the {@link PreferenceGroup} for saved group devices. + * It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} + */ +public class GroupPreviouslyConnectedDevicePreferenceController extends BasePreferenceController + implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback, BluetoothCallback { + + private static final String TAG = "GroupPreviouslyConnectedDevicePreferenceController"; + private PreferenceGroup mPreferenceGroup; + private GroupSavedBluetoothDeviceUpdater mBluetoothDeviceUpdater; + private int mPreferenceSize; + private static final int MAX_DEVICE_NUM = 3; + private DockUpdater mSavedDockUpdater; + private LocalBluetoothAdapter mLocalAdapter; + private LocalBluetoothManager manager; + + + public GroupPreviouslyConnectedDevicePreferenceController(Context context, + String preferenceKey) { + super(context, preferenceKey); + mSavedDockUpdater = FeatureFactory.getFactory(context). + getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this); + manager = Utils.getLocalBtManager(context); + if ( manager != null) { + mLocalAdapter = manager.getBluetoothAdapter(); + } + } + + @Override + public int getAvailabilityStatus() { + return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) + ) + ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceGroup = screen.findPreference(getPreferenceKey()); + mPreferenceGroup.setVisible(false); + if (isAvailable()) { + final Context context = screen.getContext(); + mBluetoothDeviceUpdater.setPrefContext(context); + mSavedDockUpdater.setPreferenceContext(context); + } + } + + @Override + public void onStart() { + mBluetoothDeviceUpdater.registerCallback(); + mSavedDockUpdater.registerCallback(); + } + + @Override + public void onStop() { + mBluetoothDeviceUpdater.unregisterCallback(); + mSavedDockUpdater.unregisterCallback(); + } + + public void init(DashboardFragment fragment) { + mBluetoothDeviceUpdater = new GroupSavedBluetoothDeviceUpdater(fragment.getContext(), + fragment, GroupPreviouslyConnectedDevicePreferenceController.this); + } + + @Override + public void onDeviceAdded(Preference preference) { + mPreferenceSize++; + if (mPreferenceSize <= MAX_DEVICE_NUM) { + mPreferenceGroup.addPreference(preference); + } + updatePreferenceVisiblity(); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceSize--; + mPreferenceGroup.removePreference(preference); + updatePreferenceVisiblity(); + } + + void updatePreferenceVisiblity() { + if ((mLocalAdapter != null) && (mLocalAdapter.getBluetoothState() + == BluetoothAdapter.STATE_ON)) { + mPreferenceGroup.setVisible(mPreferenceSize > 0); + } else { + mPreferenceGroup.setVisible(false); + } + } + + @Override + public void onBluetoothStateChanged(int bluetoothState) { + updatePreferenceVisiblity(); + } +} diff --git a/src/com/android/settings/connecteddevice/GroupSavedDeviceController.java b/src/com/android/settings/connecteddevice/GroupSavedDeviceController.java new file mode 100644 index 0000000000..5edde0819a --- /dev/null +++ b/src/com/android/settings/connecteddevice/GroupSavedDeviceController.java @@ -0,0 +1,164 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.connecteddevice; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.BluetoothDevicePreference; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.GroupBluetoothSettingsPreference; +import com.android.settings.bluetooth.GroupUtils; +import com.android.settings.bluetooth.GroupSavedBluetoothDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.GearPreference; +import com.android.settings.widget.GearPreference.OnGearClickListener; +import com.android.settings.widget.GroupPreferenceCategory; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import android.util.Log; + +import java.util.ArrayList; + +import com.android.settings.R; + +/** + * Controller to maintain the {@link PreferenceGroup} for all + * saved group devices. It uses {@link DevicePreferenceCallback} + * to add/remove {@link Preference} + */ +public class GroupSavedDeviceController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback , OnGearClickListener { + + private static final String KEY = "group_saved_device_list"; + private static final String KEY_PCG_ONE = "pcg_one"; + private static final String KEY_PCG_TWO = "pcg_two"; + private static final String KEY_PCG_THREE = "pcg_three"; + private static final String KEY_PCG_FOUR = "pcg_four"; + private static final String KEY_PCG_FIVE = "pcg_five"; + private static final String KEY_PCG_SIX = "pcg_six"; + private static final String KEY_PCG_SEVEN = "pcg_seven"; + private static final String KEY_PCG_EIGHT = "pcg_eight"; + private static final String KEY_PCG_NINE = "pcg_nine"; + private static final String KEY_PCG_REMAINING = "pcg_remaining"; + private static final String TAG = "GroupSavedDeviceController"; + private PreferenceGroup mPreferenceGroup; + private GroupSavedBluetoothDeviceUpdater mBluetoothDeviceUpdater; + private GroupBluetoothSettingsPreference mGroupPreference; + private GroupUtils mGroupUtils; + private ArrayList<GroupPreferenceCategory> mListCategories = + new ArrayList<GroupPreferenceCategory>(); + GroupPreferenceCategory mPreferenceGroupRemaining; + + public GroupSavedDeviceController(Context context) { + super(context, KEY); + DockUpdaterFeatureProvider dockUpdaterFeatureProvider = + FeatureFactory.getFactory(context).getDockUpdaterFeatureProvider(); + } + + @Override + public void onStart() { + mBluetoothDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mBluetoothDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + mPreferenceGroup = screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + mListCategories.add(screen.findPreference(KEY_PCG_ONE)); + mListCategories.add(screen.findPreference(KEY_PCG_TWO)); + mListCategories.add(screen.findPreference(KEY_PCG_THREE)); + mListCategories.add(screen.findPreference(KEY_PCG_FOUR)); + mListCategories.add(screen.findPreference(KEY_PCG_FIVE)); + mListCategories.add(screen.findPreference(KEY_PCG_SIX)); + mListCategories.add(screen.findPreference(KEY_PCG_SEVEN)); + mListCategories.add(screen.findPreference(KEY_PCG_EIGHT)); + mListCategories.add(screen.findPreference(KEY_PCG_NINE)); + mPreferenceGroupRemaining = screen.findPreference(KEY_PCG_REMAINING); + mPreferenceGroupRemaining.setGroupId(-99); + mListCategories.add(mPreferenceGroupRemaining); + + if (isAvailable()) { + final Context context = screen.getContext(); + mGroupUtils = new GroupUtils(context); + mBluetoothDeviceUpdater.setPrefContext(context); + mBluetoothDeviceUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + mGroupUtils.addPreference(mListCategories, preference, this); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mGroupUtils.removePreference(mListCategories, preference); + } + + public void init(DashboardFragment fragment) { + mBluetoothDeviceUpdater = new GroupSavedBluetoothDeviceUpdater(fragment.getContext(), + fragment, GroupSavedDeviceController.this); + } + + @Override + public void onGearClick(GearPreference p) { + GroupBluetoothSettingsPreference pre = (GroupBluetoothSettingsPreference) p; + mBluetoothDeviceUpdater.launchgroupOptions(pre); + } +} diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index 4469d265ae..749e6de898 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -28,7 +28,13 @@ import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; +import android.bluetooth.BluetoothAdapter; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settings.bluetooth.Utils; import com.android.settings.R; import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; @@ -45,7 +51,7 @@ import java.util.ArrayList; import java.util.List; public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController - implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { + implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback, BluetoothCallback { private static final String TAG = "PreviouslyDevicePreController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -58,6 +64,8 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc private final List<Preference> mDockDevicesList = new ArrayList<>(); private PreferenceGroup mPreferenceGroup; + private LocalBluetoothAdapter mLocalAdapter; + private LocalBluetoothManager manager; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; private DockUpdater mSavedDockUpdater; private BluetoothAdapter mBluetoothAdapter; @@ -82,6 +90,10 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this); mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + manager = Utils.getLocalBtManager(context); + if ( manager != null) { + mLocalAdapter = manager.getBluetoothAdapter(); + } } @Override @@ -113,6 +125,7 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mSavedDockUpdater.registerCallback(); mContext.registerReceiver(mReceiver, mIntentFilter); mBluetoothDeviceUpdater.refreshPreference(); + manager.getEventManager().registerCallback(this); } @Override @@ -120,6 +133,8 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mBluetoothDeviceUpdater.unregisterCallback(); mSavedDockUpdater.unregisterCallback(); mContext.unregisterReceiver(mReceiver); + manager.getEventManager().unregisterCallback(this); + } public void init(DashboardFragment fragment) { @@ -202,6 +217,46 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc updatePreferenceVisibility(); } + @Override + public void onBluetoothStateChanged(int bluetoothState) { + updatePreferenceVisibility(); + } + + @Override + public void onScanningStateChanged(boolean started) { + // do nothing + } + + @Override + public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { + // do nothing + } + + @Override + public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { + // do nothing + } + + @Override + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + // do nothing + } + + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { + // do nothing + } + + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + // do nothing + } + + @Override + public void onAudioModeChanged() { + // do nothing + } + @VisibleForTesting void setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater) { mBluetoothDeviceUpdater = bluetoothDeviceUpdater; @@ -226,4 +281,5 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mContext.getString(R.string.connected_device_see_all_summary)); } } + } diff --git a/src/com/android/settings/connecteddevice/SavedTwsDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedTwsDeviceGroupController.java new file mode 100644 index 0000000000..1e1f0ac3ee --- /dev/null +++ b/src/com/android/settings/connecteddevice/SavedTwsDeviceGroupController.java @@ -0,0 +1,157 @@ +/* + *Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + * Copyright (C) 2017 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; + +import android.content.pm.PackageManager; +import android.content.Context; + +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.SavedBluetoothTwsDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.DockUpdaterFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +/** + * Controller to maintain the {@link PreferenceGroup} for all + * saved TWS+ devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} + */ +public class SavedTwsDeviceGroupController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback { + + private static final String KEY = "saved_tws_device_list"; + + PreferenceGroup mPreferenceGroup; + private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private DockUpdater mSavedDockUpdater; + + public SavedTwsDeviceGroupController(Context context) { + super(context, KEY); + + DockUpdaterFeatureProvider dockUpdaterFeatureProvider = + FeatureFactory.getFactory(context).getDockUpdaterFeatureProvider(); + mSavedDockUpdater = + dockUpdaterFeatureProvider.getSavedDockUpdater(context, this); + } + + @Override + public void onStart() { + mBluetoothDeviceUpdater.registerCallback(); + mSavedDockUpdater.registerCallback(); + } + + @Override + public void onStop() { + mBluetoothDeviceUpdater.unregisterCallback(); + mSavedDockUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + if (isAvailable()) { + mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); + mBluetoothDeviceUpdater.forceUpdate(); + mSavedDockUpdater.forceUpdate(); + } + } + + @Override + public int getAvailabilityStatus() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } + + public void init(DashboardFragment fragment) { + mBluetoothDeviceUpdater = new SavedBluetoothTwsDeviceUpdater(fragment.getContext(), + fragment, SavedTwsDeviceGroupController.this); + } + + public void setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater) { + mBluetoothDeviceUpdater = bluetoothDeviceUpdater; + } + + public void setSavedDockUpdater(DockUpdater savedDockUpdater) { + mSavedDockUpdater = savedDockUpdater; + } +} diff --git a/src/com/android/settings/connecteddevice/usb/UsbBackend.java b/src/com/android/settings/connecteddevice/usb/UsbBackend.java index 244818fe16..15922005d4 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbBackend.java +++ b/src/com/android/settings/connecteddevice/usb/UsbBackend.java @@ -41,7 +41,10 @@ import java.util.List; */ public class UsbBackend { - static final int PD_ROLE_SWAP_TIMEOUT_MS = 3000; + // extend this value from 3s to 4s because of switching data role + // in USB driver side takes about 3s, plus the usb port change event + // dispatching time, 3s is not enough. + static final int PD_ROLE_SWAP_TIMEOUT_MS = 4000; static final int NONPD_ROLE_SWAP_TIMEOUT_MS = 15000; private final boolean mFileTransferRestricted; diff --git a/src/com/android/settings/core/TogglePreferenceController.java b/src/com/android/settings/core/TogglePreferenceController.java index 8b4d6d9c57..e7acc8c335 100644 --- a/src/com/android/settings/core/TogglePreferenceController.java +++ b/src/com/android/settings/core/TogglePreferenceController.java @@ -16,6 +16,7 @@ package com.android.settings.core; import android.content.Context; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.settings.overlay.FeatureFactory; @@ -64,6 +65,15 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle } @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Preference preference = screen.findPreference(getPreferenceKey()); + if (preference != null) { + preference.setOnPreferenceChangeListener(this); + } + } + + @Override public final boolean onPreferenceChange(Preference preference, Object newValue) { // TwoStatePreference is a regular preference and can be handled by DashboardFragment if (preference instanceof PrimarySwitchPreference @@ -92,4 +102,4 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle @Override public abstract int getSliceHighlightMenuRes(); -}
\ No newline at end of file +} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 804f856a13..4c9f37b424 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -67,12 +67,14 @@ import com.android.settings.biometrics.combination.CombinedBiometricProfileSetti import com.android.settings.biometrics.combination.CombinedBiometricSettings; import com.android.settings.biometrics.face.FaceSettings; import com.android.settings.biometrics.fingerprint.FingerprintSettings; +import com.android.settings.bluetooth.GroupBluetoothFragment; import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment; import com.android.settings.bluetooth.BluetoothPairingDetail; import com.android.settings.bugreporthandler.BugReportHandlerPicker; import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.GroupPreviouslyConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.usb.UsbDetailsFragment; import com.android.settings.datausage.DataSaverSummary; import com.android.settings.datausage.DataUsageList; @@ -308,9 +310,11 @@ public class SettingsGateway { WebViewAppPicker.class.getName(), LockscreenDashboardFragment.class.getName(), BluetoothDeviceDetailsFragment.class.getName(), + GroupBluetoothFragment.class.getName(), DataUsageList.class.getName(), ToggleBackupSettingFragment.class.getName(), PreviouslyConnectedDeviceDashboardFragment.class.getName(), + GroupPreviouslyConnectedDeviceDashboardFragment.class.getName(), BatterySaverScheduleSettings.class.getName(), MobileNetworkListFragment.class.getName(), PowerMenuSettings.class.getName(), diff --git a/src/com/android/settings/datausage/BillingCyclePreference.java b/src/com/android/settings/datausage/BillingCyclePreference.java index 116ed89b3c..6fc403b981 100644 --- a/src/com/android/settings/datausage/BillingCyclePreference.java +++ b/src/com/android/settings/datausage/BillingCyclePreference.java @@ -72,6 +72,7 @@ public class BillingCyclePreference extends Preference setSummary(null); setIntent(getIntent()); + updateEnabled(); } private void updateEnabled() { diff --git a/src/com/android/settings/datausage/CellDataPreference.java b/src/com/android/settings/datausage/CellDataPreference.java index 0e47bc4f2c..852f5fc31a 100644 --- a/src/com/android/settings/datausage/CellDataPreference.java +++ b/src/com/android/settings/datausage/CellDataPreference.java @@ -65,7 +65,6 @@ public class CellDataPreference extends CustomDialogPreferenceCompat final CellDataState state = (CellDataState) s; super.onRestoreInstanceState(state.getSuperState()); mChecked = state.mChecked; - mMultiSimDialog = state.mMultiSimDialog; if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { mSubId = state.mSubId; setKey(getKey() + mSubId); @@ -77,7 +76,6 @@ public class CellDataPreference extends CustomDialogPreferenceCompat protected Parcelable onSaveInstanceState() { final CellDataState state = new CellDataState(super.onSaveInstanceState()); state.mChecked = mChecked; - state.mMultiSimDialog = mMultiSimDialog; state.mSubId = mSubId; return state; } @@ -138,6 +136,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat @Override protected void performClick(View view) { final Context context = getContext(); + // TODO (b/123905421): Clean up dead code path FeatureFactory.getFactory(context).getMetricsFeatureProvider() .action(context, SettingsEnums.ACTION_CELL_DATA_TOGGLE, !mChecked); final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); @@ -178,11 +177,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat @Override protected void onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener) { - if (mMultiSimDialog) { - showMultiSimDialog(builder, listener); - } else { - showDisableDialog(builder, listener); - } + showDisableDialog(builder, listener); } private void showDisableDialog(Builder builder, @@ -193,6 +188,7 @@ public class CellDataPreference extends CustomDialogPreferenceCompat .setNegativeButton(android.R.string.cancel, null); } + // TODO (b/123905421): Clean up dead code path private void showMultiSimDialog(Builder builder, DialogInterface.OnClickListener listener) { final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); @@ -226,14 +222,8 @@ public class CellDataPreference extends CustomDialogPreferenceCompat if (which != DialogInterface.BUTTON_POSITIVE) { return; } - if (mMultiSimDialog) { - getProxySubscriptionManager().get().setDefaultDataSubId(mSubId); - setMobileDataEnabled(true); - disableDataForOtherSubscriptions(mSubId); - } else { - // TODO: extend to modify policy enabled flag. - setMobileDataEnabled(false); - } + // TODO: extend to modify policy enabled flag. + setMobileDataEnabled(false); } @VisibleForTesting @@ -259,7 +249,6 @@ public class CellDataPreference extends CustomDialogPreferenceCompat public static class CellDataState extends BaseSavedState { public int mSubId; public boolean mChecked; - public boolean mMultiSimDialog; public CellDataState(Parcelable base) { super(base); @@ -268,7 +257,6 @@ public class CellDataPreference extends CustomDialogPreferenceCompat public CellDataState(Parcel source) { super(source); mChecked = source.readByte() != 0; - mMultiSimDialog = source.readByte() != 0; mSubId = source.readInt(); } @@ -276,7 +264,6 @@ public class CellDataPreference extends CustomDialogPreferenceCompat public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeByte((byte) (mChecked ? 1 : 0)); - dest.writeByte((byte) (mMultiSimDialog ? 1 : 0)); dest.writeInt(mSubId); } diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java index a4396a286c..112ea87b94 100644 --- a/src/com/android/settings/datausage/DataUsageSummary.java +++ b/src/com/android/settings/datausage/DataUsageSummary.java @@ -27,6 +27,7 @@ import android.text.SpannableString; import android.text.TextUtils; import android.text.format.Formatter; import android.text.style.RelativeSizeSpan; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -96,7 +97,19 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage } boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context); if (hasMobileData) { - addMobileSection(defaultSubId); + List<SubscriptionInfo> subscriptions = + services.mSubscriptionManager.getActiveSubscriptionInfoList(); + if (subscriptions == null || subscriptions.size() == 0) { + addMobileSection(defaultSubId); + } + for (int i = 0; subscriptions != null && i < subscriptions.size(); i++) { + SubscriptionInfo subInfo = subscriptions.get(i); + if (subscriptions.size() > 1) { + addMobileSection(subInfo.getSubscriptionId(), subInfo); + } else { + addMobileSection(subInfo.getSubscriptionId()); + } + } if (hasActiveSubscription() && hasWifiRadio) { // If the device has active SIM, the data usage section shows usage for mobile, // and the WiFi section is added if there is a WiFi radio - legacy behavior. diff --git a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java index d698436176..58550c96c9 100644 --- a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java +++ b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java @@ -44,6 +44,11 @@ public class BluetoothSnoopLogPreferenceController extends DeveloperOptionsPrefe @VisibleForTesting static final int BTSNOOP_LOG_MODE_FULL_INDEX = 2; @VisibleForTesting + static final int BTSNOOP_LOG_MODE_SNOOPHEADERSFILTERED_INDEX = 3; + @VisibleForTesting + static final int BTSNOOP_LOG_MODE_MEDIAPKTSFILTERED_INDEX = 4; + @VisibleForTesting + static final String BLUETOOTH_BTSNOOP_LOG_MODE_PROPERTY = "persist.bluetooth.btsnooplogmode"; private final String[] mListValues; @@ -97,8 +102,13 @@ public class BluetoothSnoopLogPreferenceController extends DeveloperOptionsPrefe break; } } - listPreference.setValue(mListValues[index]); - listPreference.setSummary(mListEntries[index]); + if( index < mListValues.length && index < mListEntries.length ) { + listPreference.setValue(mListValues[index]); + listPreference.setSummary(mListEntries[index]); + } else { + Log.e(TAG, "missing some entries in xml file" + + "\t some options in developer options will not be shown until added in xml file"); + } } @Override diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index fbab1fd124..ea6d9cf5e7 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -261,6 +261,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public void onDestroyView() { super.onDestroyView(); + Context context = getContext(); + SmartDdsSwitchPreferenceController.getInstance(context).cleanUp(); unregisterReceivers(); final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -468,6 +470,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment, BluetoothA2dpConfigStore bluetoothA2dpConfigStore) { final List<AbstractPreferenceController> controllers = new ArrayList<>(); + controllers.add(new Prefer5GNetworkSummaryController(context, lifecycle)); + controllers.add(new PreferVonrController(context, lifecycle)); controllers.add(new MemoryUsagePreferenceController(context)); controllers.add(new BugReportPreferenceController(context)); controllers.add(new BugReportHandlerPreferenceController(context)); @@ -503,10 +507,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle)); controllers.add(new CameraLaserSensorPreferenceController(context)); controllers.add(new WifiDisplayCertificationPreferenceController(context)); + controllers.add(new WifiCoverageExtendPreferenceController(context)); controllers.add(new WifiVerboseLoggingPreferenceController(context)); controllers.add(new WifiScanThrottlingPreferenceController(context)); controllers.add(new WifiEnhancedMacRandomizationPreferenceController(context)); controllers.add(new MobileDataAlwaysOnPreferenceController(context)); + controllers.add(SmartDdsSwitchPreferenceController.getInstance(context)); controllers.add(new TetheringHardwareAccelPreferenceController(context)); controllers.add(new BluetoothDeviceNoNamePreferenceController(context)); controllers.add(new BluetoothAbsoluteVolumePreferenceController(context)); @@ -597,6 +603,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public void onBluetoothHDAudioEnabled(boolean enabled) { + Log.d(TAG, "onBluetoothHDAudioEnabled: " + enabled); for (AbstractPreferenceController controller : mPreferenceControllers) { if (controller instanceof AbstractBluetoothDialogPreferenceController) { ((AbstractBluetoothDialogPreferenceController) controller).onHDAudioEnabled( diff --git a/src/com/android/settings/development/Prefer5GNetworkListController.java b/src/com/android/settings/development/Prefer5GNetworkListController.java new file mode 100644 index 0000000000..ce7face9be --- /dev/null +++ b/src/com/android/settings/development/Prefer5GNetworkListController.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved + * Not a contribution + * + * 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.development; + +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_START; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.preference.PreferenceManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settingslib.core.AbstractPreferenceController; + +import com.qti.extphone.Client; +import com.qti.extphone.ExtTelephonyManager; +import com.qti.extphone.ExtPhoneCallbackBase; +import com.qti.extphone.NrConfig; +import com.qti.extphone.Status; +import com.qti.extphone.ServiceCallback; +import com.qti.extphone.Token; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This populates the entries on a page which lists all available mobile subscriptions. Each entry + * has the name of the subscription with some subtext giving additional detail, and clicking on the + * entry brings you to a details page for that network. + */ +public class Prefer5GNetworkListController extends AbstractPreferenceController implements + LifecycleObserver, ListPreference.OnPreferenceChangeListener, + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { + private static final String TAG = "Prefer5gNetworkListCtlr"; + + @VisibleForTesting + static final String KEY_ADD_MORE = "prefer_5g_add_more"; + private static final int NR_MODE_NSA_SA = 0; + private static final int NR_MODE_NSA = 1; + private static final int NR_MODE_SA = 2; + private static final int EVENT_SET_NR_CONFIG_STATUS = 101; + private static final int EVENT_GET_NR_CONFIG_STATUS = 102; + + private Client mClient; + private Context mContext; + private String mPackageName; + private SharedPreferences mSharedPreferences; + private SubscriptionManager mSubscriptionManager; + private SubscriptionsChangeListener mChangeListener; + private PreferenceScreen mPreferenceScreen; + private TelephonyManager mTelephonyManager; + private Map<Integer, ListPreference> mPreferences; + private List<ListPreference> mPreferenceList; + + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; + private int userPrefNrConfig; + private ExtTelephonyManager mExtTelephonyManager; + private boolean mServiceConnected; + private boolean mRetry; + + private ExtPhoneCallbackBase mCallback = new ExtPhoneCallbackBase() { + @Override + public void onSetNrConfig(int slotId, Token token, Status status) throws + RemoteException { + Log.d(TAG, "onSetNrConfig: slotId = " + slotId + " token = " + token + " status = " + + status); + if (status.get() == Status.SUCCESS) { + updateSharedPreference(slotId, userPrefNrConfig); + } + mMainThreadHandler.sendMessage(mMainThreadHandler + .obtainMessage(EVENT_SET_NR_CONFIG_STATUS, slotId, -1)); + } + + @Override + public void onNrConfigStatus(int slotId, Token token, Status status, NrConfig nrConfig) + throws RemoteException { + Log.d(TAG, "onNrConfigStatus: slotId = " + slotId + " token = " + token + " status = " + + status + " nrConfig = " + nrConfig); + if (status.get() == Status.SUCCESS) { + updateSharedPreference(slotId, nrConfig.get()); + mMainThreadHandler.sendMessage(mMainThreadHandler + .obtainMessage(EVENT_GET_NR_CONFIG_STATUS, slotId, -1)); + } + } + }; + + private Handler mMainThreadHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + Log.d(TAG, "handleMessage msg.what = " + msg.what); + switch (msg.what) { + case EVENT_SET_NR_CONFIG_STATUS: + case EVENT_GET_NR_CONFIG_STATUS: { + int slotId = msg.arg1; + if (mPreferenceScreen != null) { + updatePreferenceForSlot(slotId); + } + break; + } + } + } + }; + + private void updateSharedPreference(int slotId, int nrConfig) { + if (mSharedPreferences != null) { + mSharedPreferences.edit().putInt("nr_mode_" + slotId, nrConfig).apply(); + } + } + + + public Prefer5GNetworkListController(Context context, Lifecycle lifecycle) { + super(context); + Log.i(TAG, "constructor"); + mContext = context; + mPackageName = mContext.getPackageName(); + mSharedPreferences = mContext.getSharedPreferences(mContext.getPackageName(), + mContext.MODE_PRIVATE); + mSubscriptionManager = context.getSystemService(SubscriptionManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + mChangeListener = new SubscriptionsChangeListener(context, this); + mPreferences = new ArrayMap<>(); + if (mExtTelephonyManager == null) { + mExtTelephonyManager = ExtTelephonyManager.getInstance( + mContext.getApplicationContext()); + } + Log.d(TAG, "Connect to ExtTelephony bound service..."); + mExtTelephonyManager.connectService(mServiceCallback); + + lifecycle.addObserver(this); + } + + private ServiceCallback mServiceCallback = new ServiceCallback() { + @Override + public void onConnected() { + Log.d(TAG, "ExtTelephony Service connected"); + mServiceConnected = true; + mClient = mExtTelephonyManager.registerCallback(mPackageName, mCallback); + Log.d(TAG, "Client = " + mClient); + if (mRetry) { + for (int slotId = 0; slotId < mTelephonyManager.getPhoneCount(); + slotId++) { + mRetry = false; + Token token = mExtTelephonyManager.queryNrConfig(slotId, mClient); + Log.d(TAG, "queryNrConfig: " + token); + } + } + } + + @Override + public void onDisconnected() { + Log.d(TAG, "ExtTelephony Service disconnected..."); + if (mServiceConnected) { + mServiceConnected = false; + mClient = null; + } + } + }; + + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + Log.i(TAG, "onResume"); + mChangeListener.start(); + update(); + } + + @OnLifecycleEvent(ON_PAUSE) + public void onPause() { + mChangeListener.stop(); + } + + @OnLifecycleEvent(ON_DESTROY) + protected void onDestroy() { + Log.d(TAG, "onDestroy"); + mExtTelephonyManager.unRegisterCallback(mCallback); + mExtTelephonyManager.disconnectService(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Log.i(TAG, "displayPreference"); + mPreferenceScreen = screen; + mPreferenceScreen.findPreference(KEY_ADD_MORE).setVisible( + MobileNetworkUtils.showEuiccSettings(mContext)); + } + + private void update() { + if (mPreferenceScreen == null) { + return; + } + + // Since we may already have created some preferences previously, we first grab the list of + // those, then go through the current available subscriptions making sure they are all + // present in the screen, and finally remove any now-outdated ones. + final Map<Integer, ListPreference> existingPreferences = mPreferences; + mPreferences = new ArrayMap<>(); + mPreferenceList = new ArrayList<>(); + + for (int slotId = 0; slotId < mTelephonyManager.getPhoneCount(); + slotId++) { + SubscriptionInfo info = mSubscriptionManager. + getActiveSubscriptionInfoForSimSlotIndex(slotId); + if (info != null) { + final int subId = info.getSubscriptionId(); + ListPreference pref = existingPreferences.remove(subId); + if (pref == null) { + pref = new ListPreference(mPreferenceScreen.getContext()); + mPreferenceScreen.addPreference(pref); + } + pref.setTitle(info.getDisplayName()); + pref.setOrder(slotId); + pref.setDialogTitle("Select NR Mode For Slot " + slotId); + + pref.setOnPreferenceChangeListener(this); + pref.setEntries(R.array.preferred_5g_network_mode_choices); + pref.setEntryValues(R.array.preferred_5g_network_mode_values); + if (mServiceConnected && mClient != null) { + mRetry = false; + Token token = mExtTelephonyManager.queryNrConfig(slotId, mClient); + Log.d(TAG, "queryNrConfig: " + token); + } else { + mRetry = true; + } + + mPreferences.put(subId, pref); + mPreferenceList.add(pref); + } else { + Log.d(TAG, "sub info is null, add null preference for slot: " + slotId); + mPreferenceList.add(slotId, null); + } + } + for (Preference pref : existingPreferences.values()) { + mPreferenceScreen.removePreference(pref); + } + } + + private void updatePreferenceForSlot(int slotId) { + int nrConfig = mSharedPreferences.getInt("nr_mode_" + slotId, + NrConfig.NR_CONFIG_COMBINED_SA_NSA); + String text = mContext.getString(getSummaryResId(nrConfig)); + Log.d(TAG, "updatePreferenceForSlot for " + slotId + " ,nr mode is " + nrConfig + + " , set summary to " + text); + ListPreference pref = mPreferenceList.get(slotId); + if (pref != null) { + pref.setSummary(text); + pref.setValue(Integer.toString(nrConfig)); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object object) { + final int newNrMode = Integer.parseInt((String) object); + final int slotId = mPreferenceList.indexOf(preference); + Log.i(TAG, "onPreferenceChange for slot: " + slotId + ", setNrConfig: " + newNrMode); + userPrefNrConfig = newNrMode; + if (mServiceConnected && mClient != null) { + Token token = mExtTelephonyManager.setNrConfig( + slotId, new NrConfig(newNrMode), mClient); + Log.d(TAG, "setNrConfig: " + token); + } + final ListPreference listPreference = (ListPreference) preference; + String summary = mContext.getString(getSummaryResId(newNrMode)); + listPreference.setSummary(summary); + return true; + } + + private int getSummaryResId(int nrMode) { + if (nrMode == NrConfig.NR_CONFIG_COMBINED_SA_NSA) { + return R.string.nr_nsa_sa; + } else if (nrMode == NrConfig.NR_CONFIG_NSA) { + return R.string.nr_nsa; + } else if (nrMode == NrConfig.NR_CONFIG_SA) { + return R.string.nr_sa; + } else { + return R.string.nr_nsa_sa; + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return null; + } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + } + + @Override + public void onSubscriptionsChanged() { + boolean isSubChanged = false; + final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions( + mContext); + for (SubscriptionInfo info : subs) { + if (info == null) return; + + final int subId = info.getSubscriptionId(); + if (mPreferences.get(subId) == null) { + isSubChanged = true; + } + } + + if (isSubChanged) { + Log.d(TAG, "sub changed, will update preference"); + update(); + } + } +} diff --git a/src/com/android/settings/development/Prefer5GNetworkListFragment.java b/src/com/android/settings/development/Prefer5GNetworkListFragment.java new file mode 100644 index 0000000000..836c159e0c --- /dev/null +++ b/src/com/android/settings/development/Prefer5GNetworkListFragment.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved + * Not a contribution + * + * 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.development; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.UserManager; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) +public class Prefer5GNetworkListFragment extends DashboardFragment { + private static final String LOG_TAG = "Prefer5GNetworkListFragment"; + + @Override + protected int getPreferenceScreenResId() { + return R.xml.developer_mobile_network_list; + } + + @Override + protected String getLogTag() { + return LOG_TAG; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.MOBILE_NETWORK_LIST; + } + + @Override + protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { + final List<AbstractPreferenceController> controllers = new ArrayList<>(); + controllers.add(new Prefer5GNetworkListController(getContext(), getLifecycle())); + return controllers; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.developer_mobile_network_list) { + + @Override + protected boolean isPageSearchEnabled(Context context) { + return context.getSystemService(UserManager.class).isAdminUser(); + } + }; +} diff --git a/src/com/android/settings/development/Prefer5GNetworkSummaryController.java b/src/com/android/settings/development/Prefer5GNetworkSummaryController.java new file mode 100644 index 0000000000..b2cec8722c --- /dev/null +++ b/src/com/android/settings/development/Prefer5GNetworkSummaryController.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved + * Not a contribution + * + * 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.development; + +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; + +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.euicc.EuiccManager; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.AddPreference; +import com.android.settingslib.Utils; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +import java.util.List; + +public class Prefer5GNetworkSummaryController extends AbstractPreferenceController implements + SubscriptionsChangeListener.SubscriptionsChangeListenerClient, LifecycleObserver, + PreferenceControllerMixin { + private static final String TAG = "Prefer5GNetSummaryCtlr"; + + private static final String KEY = "prefer_5G_nr_mode"; + + private final MetricsFeatureProvider mMetricsFeatureProvider; + + private SubscriptionManager mSubscriptionManager; + private UserManager mUserManager; + private SubscriptionsChangeListener mChangeListener; + private AddPreference mPreference; + + public Prefer5GNetworkSummaryController(Context context, Lifecycle lifecycle) { + super(context); + mMetricsFeatureProvider = FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(); + mSubscriptionManager = context.getSystemService(SubscriptionManager.class); + mUserManager = context.getSystemService(UserManager.class); + if (lifecycle != null) { + mChangeListener = new SubscriptionsChangeListener(context, this); + lifecycle.addObserver(this); + } + } + + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + mChangeListener.start(); + update(); + } + + @OnLifecycleEvent(ON_PAUSE) + public void onPause() { + mChangeListener.stop(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public CharSequence getSummary() { + final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions( + mContext); + if (subs.isEmpty()) { + if (MobileNetworkUtils.showEuiccSettings(mContext)) { + return mContext.getResources().getString( + R.string.mobile_network_summary_add_a_network); + } + return null; + } else { + final int count = subs.size(); + return mContext.getResources().getQuantityString(R.plurals.mobile_network_summary_count, + count, count); + } + } + + private void startAddSimFlow() { + final Intent intent = new Intent(EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION); + intent.putExtra(EuiccManager.EXTRA_FORCE_PROVISION, true); + mContext.startActivity(intent); + } + + private void update() { + if (mPreference == null || mPreference.isDisabledByAdmin()) { + return; + } + refreshSummary(mPreference); + mPreference.setOnPreferenceClickListener(null); + mPreference.setOnAddClickListener(null); + mPreference.setFragment(null); + mPreference.setEnabled(!mChangeListener.isAirplaneModeOn()); + + final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions( + mContext); + + if (subs.isEmpty()) { + if (MobileNetworkUtils.showEuiccSettings(mContext)) { + mPreference.setOnPreferenceClickListener((Preference pref) -> { + mMetricsFeatureProvider.logClickedPreference(pref, + pref.getExtras().getInt(DashboardFragment.CATEGORY)); + startAddSimFlow(); + return true; + }); + } else { + mPreference.setEnabled(false); + } + } else { + // We have one or more existing subscriptions, so we want the plus button if eSIM is + // supported. + if (MobileNetworkUtils.showEuiccSettings(mContext)) { + mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn()); + mPreference.setOnAddClickListener(p -> { + mMetricsFeatureProvider.logClickedPreference(p, + p.getExtras().getInt(DashboardFragment.CATEGORY)); + startAddSimFlow(); + }); + } + + mPreference.setFragment(Prefer5GNetworkListFragment.class.getCanonicalName()); + } + } + + @Override + public boolean isAvailable() { + return !Utils.isWifiOnly(mContext) && mUserManager.isAdminUser(); + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + update(); + } + + @Override + public void onSubscriptionsChanged() { + refreshSummary(mPreference); + update(); + } +} diff --git a/src/com/android/settings/development/PreferVonrController.java b/src/com/android/settings/development/PreferVonrController.java new file mode 100644 index 0000000000..6eab15e5b5 --- /dev/null +++ b/src/com/android/settings/development/PreferVonrController.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved + * Not a contribution + + * 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.development; + +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; + +import android.content.Context; +import android.os.UserManager; +import android.telephony.SubscriptionInfo; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +import com.android.settings.R; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.SubscriptionUtil; +import com.android.settingslib.Utils; + +import java.util.List; + +public class PreferVonrController extends DeveloperOptionsPreferenceController implements + SubscriptionsChangeListener.SubscriptionsChangeListenerClient, LifecycleObserver{ + private static final String TAG = "PreferVonrCtlr"; + + private static final String KEY = "prefer_vonr_mode"; + + + private UserManager mUserManager; + private SubscriptionsChangeListener mChangeListener; + private Preference mPreference; + + public PreferVonrController(Context context, Lifecycle lifecycle) { + super(context); + mUserManager = context.getSystemService(UserManager.class); + mChangeListener = new SubscriptionsChangeListener(context, this); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + mChangeListener.start(); + update(); + } + + @OnLifecycleEvent(ON_PAUSE) + public void onPause() { + mChangeListener.stop(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + private void update() { + if (mPreference == null) { + return; + } + mPreference.setEnabled(!mChangeListener.isAirplaneModeOn()); + + final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions( + mContext); + + if (subs.isEmpty()) { + mPreference.setEnabled(false); + } + } + + @Override + public boolean isAvailable() { + return !Utils.isWifiOnly(mContext) && mUserManager.isAdminUser(); + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + update(); + } + + @Override + public void onSubscriptionsChanged() { + update(); + } +} diff --git a/src/com/android/settings/development/PreferVonrSettings.java b/src/com/android/settings/development/PreferVonrSettings.java new file mode 100644 index 0000000000..34e6cbdb4d --- /dev/null +++ b/src/com/android/settings/development/PreferVonrSettings.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved + * Not a contribution + + * 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.development; + +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.SharedPreferences; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.os.Bundle; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.ims.ImsException; +import com.android.ims.ImsManager; +import com.android.settings.R; +import com.android.settings.network.ims.VolteQueryImsState; +import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settingslib.RestrictedSwitchPreference; +import com.android.settings.SettingsPreferenceFragment; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class PreferVonrSettings extends SettingsPreferenceFragment implements + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { + private static final String TAG = "PreferVonrSettings"; + private static final int VONR_MODE_INVALID = -1; + private static final int VONR_MODE_OFF = 0; + private static final int VONR_MODE_ON = 1; + private static final String VONR_MODE_KEY = "vonr_mode_"; + private SubscriptionManager mSubscriptionManager; + private SubscriptionsChangeListener mChangeListener; + private TelephonyManager mTelephonyManager; + private Map<Integer, RestrictedSwitchPreference> mPreferences; + private Context mContext; + private PreferenceScreen mPreferenceScreen; + private SharedPreferences mSharedPreferences; + private final List<RestrictedSwitchPreference> mPreferenceList = new ArrayList<>(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate"); + if(getPreferenceScreen() == null) { + setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext())); + } + mPreferenceScreen = getPreferenceScreen(); + addPreferencesFromResource(R.xml.prefer_vonr_list); + mContext = getPrefContext(); + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + mChangeListener = new SubscriptionsChangeListener(mContext, this); + mPreferences = new ArrayMap<>(); + mSharedPreferences = mContext.getSharedPreferences(mContext.getPackageName(), + mContext.MODE_PRIVATE); + } + + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + Log.d(TAG, "onResume"); + super.onResume(); + mChangeListener.start(); + update(); + } + + @OnLifecycleEvent(ON_PAUSE) + public void onPause() { + super.onPause(); + mChangeListener.stop(); + } + + private void update() { + Log.d(TAG, "update"); + // Since we may already have created some preferences previously, we first grab the list of + // those, then go through the current available subscriptions making sure they are all + // present in the screen, and finally remove any now-outdated ones. + final Map<Integer, RestrictedSwitchPreference> existingPreferences = mPreferences; + mPreferences = new ArrayMap<>(); + + for (int slotId = 0; slotId < mTelephonyManager.getActiveModemCount(); + slotId++) { + SubscriptionInfo info = mSubscriptionManager. + getActiveSubscriptionInfoForSimSlotIndex(slotId); + if (info != null) { + final int subId = info.getSubscriptionId(); + RestrictedSwitchPreference pref = existingPreferences.remove(subId); + if (pref == null) { + pref = new RestrictedSwitchPreference(mContext); + mPreferenceScreen.addPreference(pref); + } + pref.setTitle(info.getDisplayName()); + pref.setOrder(slotId); + + mPreferences.put(subId, pref); + mPreferenceList.add(pref); + pref.setChecked(isVoNrSwitchChecked(slotId)); + pref.setEnabled(isVoNrSwitchEnabled(subId, slotId)); + maybeChangeNrCapability(slotId); + Log.d(TAG, "add preference for slot: " + slotId + " subId: " + subId); + } else { + Log.d(TAG, "sub info is null, add null preference for slot: " + slotId); + mPreferenceList.add(slotId, null); + } + } + for (Preference pref : existingPreferences.values()) { + mPreferenceScreen.removePreference(pref); + } + } + + /** + * @return {@code true} if VoIMS opt-in has been enabled + * or VoLTE can be perform on this subscription, + * or VoNR is not supported by platform, + * {@code false} otherwise. + */ + private boolean isVoNrSwitchEnabled(int subId, int slotId) { + ImsManager imsMgr = ImsManager.getInstance(mContext, slotId); + boolean isVoNrEnabledByCarrier = imsMgr.isImsOverNrEnabledByPlatform(); + VolteQueryImsState queryImsState = new VolteQueryImsState(mContext, subId); + return (queryImsState.isVoImsOptInEnabled() || queryImsState.isReadyToVoLte()) + && !isVoNrEnabledByCarrier; + } + + /** + * @return {@code false} if VoLTE is off. + * @return {@code true} if VoLTE is on and supporting SA in carrier config. + * @return what user choose in this UI if VoLTE is on, but not supporting + * SA in carrier config + */ + private boolean isVoNrSwitchChecked(int slotId) { + ImsManager imsMgr = ImsManager.getInstance(mContext, slotId); + boolean isVolteEnabled = imsMgr.isEnhanced4gLteModeSettingEnabledByUser(); + if (!isVolteEnabled) { + return false; + } + boolean isVoNrEnabledByCarrier = imsMgr.isImsOverNrEnabledByPlatform(); + int vonrMode = mSharedPreferences.getInt(VONR_MODE_KEY + slotId, VONR_MODE_INVALID); + boolean isVoNrEnabledByUser = vonrMode == VONR_MODE_ON; + Log.d(TAG, "enhanced 4g enabled: " + isVolteEnabled + ", vonr enabled by carrier: " + + isVoNrEnabledByCarrier + ", vonr enabled by user: " + isVoNrEnabledByUser + + " for slot: " + slotId); + return isVoNrEnabledByCarrier ? isVolteEnabled : isVoNrEnabledByUser; + } + + /** + * This function is to make sure UI align with real capabilities. + * isVolteEnabled: the user option of "Enhanced 4g", true if enabled. + * isVoNrEnabledByCarrier: defined by carrier_nr_availabilities_int_array, true if + * SA is supported. + * isVoNrEnabledByUser: the user option of this UI, true if enabled. + * For china operator's sub, need this UI to enable/disable VoNR. + * For other subs, VoNR capability is been combined with VoLTE through "Enhanced 4g" + * option. We will not change NR capability here for other subs. + */ + private void maybeChangeNrCapability(int slotId) { + ImsManager imsMgr = ImsManager.getInstance(mContext, slotId); + boolean isVolteEnabled = imsMgr.isEnhanced4gLteModeSettingEnabledByUser(); + boolean isVoNrEnabledByCarrier = imsMgr.isImsOverNrEnabledByPlatform(); + int vonrMode = mSharedPreferences.getInt(VONR_MODE_KEY + slotId, VONR_MODE_INVALID); + boolean isVoNrEnabledByUser = vonrMode == VONR_MODE_ON; + // If VoLTE is disabled, need to make sure VoNR is also disabled. + if (!isVolteEnabled && isVoNrEnabledByUser && !isVoNrEnabledByCarrier) { + changeNrCapability(imsMgr, false); + } + } + + @Override + public boolean onPreferenceTreeClick(Preference preference) { + final int slotId = mPreferenceList.indexOf(preference); + RestrictedSwitchPreference pref = (RestrictedSwitchPreference)preference; + Log.d(TAG, "onPreferenceTreeClick, preference isChecked: " + pref.isChecked() + + " for slot: " + slotId); + + ImsManager imsMgr = ImsManager.getInstance(mContext, slotId); + boolean isImsEnabled = imsMgr.isEnhanced4gLteModeSettingEnabledByUser(); + if (!isImsEnabled) { + Log.d(TAG, "onPreferenceTreeClick, ims is disabled, ignore the request"); + return false; + } + changeNrCapability(imsMgr, pref.isChecked()); + savePreferenceForSlot(slotId, pref.isChecked()); + return super.onPreferenceTreeClick(preference); + } + + private void changeNrCapability(ImsManager imsMgr, boolean enabled) { + try { + imsMgr.changeMmTelCapability(enabled, + MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + ImsRegistrationImplBase.REGISTRATION_TECH_NR); + } catch (ImsException e) { + Log.e(TAG, "Failed to change vonr mode to " + enabled + " since " + e); + } + } + + private void savePreferenceForSlot(int slotId, boolean isChecked) { + if (mSharedPreferences != null) { + int vonr_mode = isChecked? VONR_MODE_ON: VONR_MODE_OFF; + mSharedPreferences.edit().putInt("vonr_mode_" + slotId, vonr_mode).apply(); + } + } + + @Override + public void onSubscriptionsChanged() { + final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions( + mContext); + for (SubscriptionInfo info : subs) { + if (info == null) return; + + final int subId = info.getSubscriptionId(); + if (mPreferences.get(subId) == null) { + Log.d(TAG, "sub changed, will update preference"); + update(); + break; + } + } + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.DEVELOPMENT; + } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + } +} diff --git a/src/com/android/settings/development/SmartDdsSwitchPreferenceController.java b/src/com/android/settings/development/SmartDdsSwitchPreferenceController.java new file mode 100644 index 0000000000..0a3a167dd0 --- /dev/null +++ b/src/com/android/settings/development/SmartDdsSwitchPreferenceController.java @@ -0,0 +1,242 @@ +/* +Copyright (c) 2021 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.android.settings.development; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; +import com.android.settings.R; + +import com.qti.extphone.Client; +import com.qti.extphone.ExtPhoneCallbackBase; +import com.qti.extphone.ExtTelephonyManager; +import com.qti.extphone.ServiceCallback; +import com.qti.extphone.Token; + +public class SmartDdsSwitchPreferenceController extends DeveloperOptionsPreferenceController + implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { + + private final String TAG = "SmartDdsSwitchPreferenceController"; + + private final int EVENT_SET_DEFAULT_TOGGLE_STATE_RESPONSE = 1; + + private final int SETTING_VALUE_ON = 1; + private final int SETTING_VALUE_OFF = 0; + + private static SmartDdsSwitchPreferenceController mInstance; + private Client mClient; + private Context mContext; + private String mPackageName; + private final TelephonyManager mTelephonyManager; + private ExtTelephonyManager mExtTelephonyManager; + private boolean mFeatureAvailable = false; + private boolean mServiceConnected = false; + private boolean mSwitchEnabled = false; + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateState(mPreference); + } + }; + + private SmartDdsSwitchPreferenceController(Context context) { + super(context); + Log.d(TAG, "Constructor"); + mContext = context.getApplicationContext(); + mPackageName = mContext.getPackageName(); + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + mExtTelephonyManager = ExtTelephonyManager.getInstance(mContext); + mExtTelephonyManager.connectService(mExtTelManagerServiceCallback); + IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, filter); + } + + public static SmartDdsSwitchPreferenceController getInstance(Context context) { + if (mInstance == null) { + mInstance = new SmartDdsSwitchPreferenceController(context); + } + return mInstance; + } + + public void cleanUp() { + Log.d(TAG, "Disconnecting ExtTelephonyService"); + mExtTelephonyManager.disconnectService(); + mInstance = null; + } + + private ServiceCallback mExtTelManagerServiceCallback = new ServiceCallback() { + @Override + public void onConnected() { + Log.d(TAG, "ExtTelephonyService connected"); + mServiceConnected = true; + mClient = mExtTelephonyManager.registerCallback(mPackageName, mCallback); + Log.d(TAG, "Client = " + mClient); + } + + @Override + public void onDisconnected() { + Log.d(TAG, "ExtTelephonyService disconnected"); + mContext.unregisterReceiver(mBroadcastReceiver); + mServiceConnected = false; + mClient = null; + } + }; + + private ExtPhoneCallbackBase mCallback = new ExtPhoneCallbackBase() { + @Override + public void setSmartDdsSwitchToggleResponse(Token token, boolean result) throws + RemoteException { + Log.d(TAG, "setSmartDdsSwitchToggleResponse: token = " + token + " result = " + result); + if (result) { + mHandler.sendMessage(mHandler.obtainMessage( + EVENT_SET_DEFAULT_TOGGLE_STATE_RESPONSE)); + } + } + }; + + private Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case EVENT_SET_DEFAULT_TOGGLE_STATE_RESPONSE: { + Log.d(TAG, "EVENT_SET_DEFAULT_TOGGLE_STATE_RESPONSE"); + String defaultSummary = mContext.getResources().getString( + R.string.smart_dds_switch_summary); + updateUi(defaultSummary, true); + putSwitchValue(mSwitchEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + break; + } + default: + Log.e(TAG, "Unsupported action"); + } + } + }; + + @Override + public String getPreferenceKey() { + return Settings.Global.SMART_DDS_SWITCH; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mSwitchEnabled = (Boolean) newValue; + Log.d(TAG, "onPreferenceChange: isEnabled = " + mSwitchEnabled); + // Temporarily update the text and disable the button until the response is received + String waitSummary = mContext.getResources().getString( + R.string.smart_dds_switch_wait_summary); + updateUi(waitSummary, false); + if (mServiceConnected && mClient != null) { + try { + mExtTelephonyManager.setSmartDdsSwitchToggle(mSwitchEnabled, mClient); + return true; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + return false; + } + } else { + Log.e(TAG, "ExtTelephonyManager service not connected"); + return false; + } + } + + @Override + public void updateState(Preference preference) { + if (mPreference != null) { + final int smartDdsSwitch = getSwitchValue(); + ((SwitchPreference) mPreference).setChecked(smartDdsSwitch != SETTING_VALUE_OFF); + } + if (mServiceConnected) { + try { + mFeatureAvailable = mExtTelephonyManager.isSmartDdsSwitchFeatureAvailable(); + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + Log.d(TAG, "mFeatureAvailable: " + mFeatureAvailable); + if (mFeatureAvailable) { + String defaultSummary = mContext.getResources().getString( + R.string.smart_dds_switch_summary); + updateUi(defaultSummary, isAvailable()); + } else { + Log.d(TAG, "Feature unavailable"); + preference.setVisible(false); + } + } else { + Log.d(TAG, "Service not connected"); + } + } + + private void updateUi(String summary, boolean enable) { + Log.d(TAG, "updateUi enable: " + enable); + if (mPreference != null) { + ((SwitchPreference) mPreference).setVisible(true); + ((SwitchPreference) mPreference).setSummary(summary); + ((SwitchPreference) mPreference).setEnabled(enable); + } + } + + @Override + public boolean isAvailable() { + // Only show the toggle if 1) APM is off and 2) more than one subscription is active + SubscriptionManager subscriptionManager = mContext.getSystemService( + SubscriptionManager.class); + int numActiveSubscriptionInfoCount = subscriptionManager.getActiveSubscriptionInfoCount(); + Log.d(TAG, "numActiveSubscriptionInfoCount: " + numActiveSubscriptionInfoCount); + return !isAirplaneModeOn() && (numActiveSubscriptionInfoCount > 1); + } + + private boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + + private void putSwitchValue(int state) { + Settings.Global.putInt(mContext.getContentResolver(), getPreferenceKey(), state); + } + + private int getSwitchValue() { + return Settings.Global.getInt(mContext.getContentResolver(), getPreferenceKey(), + SETTING_VALUE_OFF); + } +}
\ No newline at end of file diff --git a/src/com/android/settings/development/WifiCoverageExtendPreferenceController.java b/src/com/android/settings/development/WifiCoverageExtendPreferenceController.java new file mode 100644 index 0000000000..9f974ca4c6 --- /dev/null +++ b/src/com/android/settings/development/WifiCoverageExtendPreferenceController.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.settings.development; + +import android.content.Context; +import android.net.wifi.WifiManager; +import androidx.annotation.VisibleForTesting; +import androidx.preference.SwitchPreference; +import androidx.preference.Preference; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class WifiCoverageExtendPreferenceController extends DeveloperOptionsPreferenceController + implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { + + private static final String WIFI_COVERAGE_EXTEND_KEY = "wifi_coverage_extend"; + + private final WifiManager mWifiManager; + + public WifiCoverageExtendPreferenceController(Context context) { + super(context); + + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + } + + @Override + public String getPreferenceKey() { + return WIFI_COVERAGE_EXTEND_KEY; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + mWifiManager.enableWifiCoverageExtendFeature(isEnabled); + return true; + } + + @Override + public void updateState(Preference preference) { + final boolean enabled = mWifiManager.isWifiCoverageExtendFeatureEnabled(); + ((SwitchPreference) mPreference).setChecked(enabled); + + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + mWifiManager.enableWifiCoverageExtendFeature(false); + ((SwitchPreference) mPreference).setChecked(false); + } +} diff --git a/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java b/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java index d66b8d8dc8..0367fcca22 100644 --- a/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java +++ b/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java @@ -38,7 +38,9 @@ public abstract class AbstractBluetoothDialogPreferenceController extends private static final String TAG = "AbstractBtDlgCtr"; - protected static final int[] CODEC_TYPES = {BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + protected static final int[] CODEC_TYPES = {BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE, BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, @@ -162,6 +164,11 @@ public abstract class AbstractBluetoothDialogPreferenceController extends Log.d(TAG, "Unable to get current codec config. Codec status is null"); return null; } + if (codecStatus.getCodecConfig().getCodecType() >= + BluetoothCodecConfig.SOURCE_QVA_CODEC_TYPE_MAX) { + Log.d(TAG,"Invalid codec type"); + return null; + } return codecStatus.getCodecConfig(); } @@ -219,11 +226,18 @@ public abstract class AbstractBluetoothDialogPreferenceController extends */ public void onHDAudioEnabled(boolean enabled) {} - static int getHighestCodec(BluetoothCodecConfig[] configs) { + static int getHighestCodec(BluetoothA2dp bluetoothA2dp, BluetoothDevice activeDevice, + BluetoothCodecConfig[] configs) { if (configs == null) { Log.d(TAG, "Unable to get highest codec. Configs are empty"); return BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; } + Log.d(TAG, "CODEC_TYPES len: " + CODEC_TYPES.length + " codec_config len: " + configs.length); + // If HD audio is not enabled, SBC is the only one available codec. + if (bluetoothA2dp.isOptionalCodecsEnabled(activeDevice) + != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { + return BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC; + } for (int i = 0; i < CODEC_TYPES.length; i++) { for (int j = 0; j < configs.length; j++) { if ((configs[j].getCodecType() == CODEC_TYPES[i])) { diff --git a/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreference.java b/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreference.java index 6a733f3fba..6178ed1937 100644 --- a/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreference.java +++ b/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreference.java @@ -22,12 +22,16 @@ import android.widget.RadioGroup; import com.android.settings.R; +import android.util.Log; + /** * Dialog preference to set the Bluetooth A2DP config of codec */ public class BluetoothCodecDialogPreference extends BaseBluetoothDialogPreference implements RadioGroup.OnCheckedChangeListener { + private static final String TAG = "BtCodecDlgPref"; + public BluetoothCodecDialogPreference(Context context) { super(context); initialize(context); @@ -60,13 +64,19 @@ public class BluetoothCodecDialogPreference extends BaseBluetoothDialogPreferenc mRadioButtonIds.add(R.id.bluetooth_audio_codec_aac); mRadioButtonIds.add(R.id.bluetooth_audio_codec_aptx); mRadioButtonIds.add(R.id.bluetooth_audio_codec_aptx_hd); + mRadioButtonIds.add(R.id.bluetooth_audio_codec_aptx_adaptive); mRadioButtonIds.add(R.id.bluetooth_audio_codec_ldac); + mRadioButtonIds.add(R.id.bluetooth_audio_codec_aptx_twsp); String[] stringArray = context.getResources().getStringArray( R.array.bluetooth_a2dp_codec_titles); + + Log.e(TAG, "a2dp_codec_titles array length: " + stringArray.length); for (int i = 0; i < stringArray.length; i++) { mRadioButtonStrings.add(stringArray[i]); } + stringArray = context.getResources().getStringArray(R.array.bluetooth_a2dp_codec_summaries); + Log.e(TAG, "a2dp_codec_summaries array length: " + stringArray.length); for (int i = 0; i < stringArray.length; i++) { mSummaryStrings.add(stringArray[i]); } diff --git a/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceController.java b/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceController.java index 6b243c600f..c8c9241b92 100644 --- a/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceController.java +++ b/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceController.java @@ -93,8 +93,9 @@ public class BluetoothCodecDialogPreferenceController extends int codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; switch (index) { case 0: - codecTypeValue = getHighestCodec(getSelectableConfigs( - mBluetoothA2dp.getActiveDevice())); + final BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice(); + codecTypeValue = getHighestCodec(mBluetoothA2dp, activeDevice, + getSelectableConfigs(activeDevice)); codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST; break; case 1: @@ -117,6 +118,14 @@ public class BluetoothCodecDialogPreferenceController extends codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC; codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST; break; + case 6: + codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; + codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST; + break; + case 7: + codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP; + codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST; + break; default: break; } @@ -147,6 +156,11 @@ public class BluetoothCodecDialogPreferenceController extends mCallback.onBluetoothCodecChanged(); } + @Override + public void onHDAudioEnabled(boolean enabled) { + writeConfigurationValues(/* index= */ 0); + } + private List<Integer> getIndexFromConfig(BluetoothCodecConfig[] configs) { List<Integer> indexArray = new ArrayList<>(); for (int i = 0; i < configs.length; i++) { @@ -171,9 +185,15 @@ public class BluetoothCodecDialogPreferenceController extends case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: index = 4; break; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: + index = 6; + break; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: index = 5; break; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP: + index = 7; + break; default: Log.e(TAG, "Unsupported config:" + config); break; diff --git a/src/com/android/settings/development/bluetooth/BluetoothHDAudioPreferenceController.java b/src/com/android/settings/development/bluetooth/BluetoothHDAudioPreferenceController.java index d4ca4e5795..6481c759f7 100644 --- a/src/com/android/settings/development/bluetooth/BluetoothHDAudioPreferenceController.java +++ b/src/com/android/settings/development/bluetooth/BluetoothHDAudioPreferenceController.java @@ -81,6 +81,7 @@ public class BluetoothHDAudioPreferenceController extends AbstractBluetoothPrefe return true; } final boolean enabled = (Boolean) newValue; + Log.e(TAG, "onPreferenceChange: " + enabled); final int prefValue = enabled ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; diff --git a/src/com/android/settings/development/bluetooth/BluetoothQualityDialogPreferenceController.java b/src/com/android/settings/development/bluetooth/BluetoothQualityDialogPreferenceController.java index 4b38e11977..f4d685733b 100644 --- a/src/com/android/settings/development/bluetooth/BluetoothQualityDialogPreferenceController.java +++ b/src/com/android/settings/development/bluetooth/BluetoothQualityDialogPreferenceController.java @@ -105,7 +105,8 @@ public class BluetoothQualityDialogPreferenceController extends @Override public void onHDAudioEnabled(boolean enabled) { - mPreference.setEnabled(false); + Log.d(TAG, "onHDAudioEnabled: " + enabled); + mPreference.setEnabled(enabled); } @VisibleForTesting diff --git a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java index e6d9dfdbb7..1d89e2e6d6 100644 --- a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java +++ b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java @@ -30,6 +30,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.bluetooth.BluetoothLengthDeviceNameFilter; import com.android.settings.core.BasePreferenceController; import com.android.settings.widget.ValidatedEditTextPreference; @@ -78,6 +79,9 @@ public class DeviceNamePreferenceController extends BasePreferenceController private void initializeDeviceName() { mDeviceName = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME); + if (Utils.isSupportCTPA(mContext)) { + mDeviceName = Utils.getString(mContext, Utils.KEY_DEVICE_NAME); + } if (mDeviceName == null) { mDeviceName = Build.MODEL; } @@ -138,6 +142,10 @@ public class DeviceNamePreferenceController extends BasePreferenceController private void setSettingsGlobalDeviceName(String deviceName) { Settings.Global.putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME, deviceName); + if (Utils.isSupportCTPA(mContext)) { + Settings.Global.putString(mContext.getContentResolver(), Utils.KEY_DEVICE_NAME, + deviceName); + } } private void setBluetoothDeviceName(String deviceName) { diff --git a/src/com/android/settings/deviceinfo/HardwareInfoPreferenceController.java b/src/com/android/settings/deviceinfo/HardwareInfoPreferenceController.java index 5f760bfa00..7bc272e8c2 100644 --- a/src/com/android/settings/deviceinfo/HardwareInfoPreferenceController.java +++ b/src/com/android/settings/deviceinfo/HardwareInfoPreferenceController.java @@ -22,6 +22,7 @@ import android.util.Log; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.DeviceInfoUtils; @@ -49,6 +50,14 @@ public class HardwareInfoPreferenceController extends BasePreferenceController { @Override public CharSequence getSummary() { + if (Utils.isSupportCTPA(mContext)) { + String modelName = Utils.getString(mContext, Utils.KEY_MODEL); + if (null == modelName || modelName.isEmpty()) { + modelName = getDeviceModel(); + } + return modelName; + } + return getDeviceModel(); } diff --git a/src/com/android/settings/deviceinfo/ImsConnector.java b/src/com/android/settings/deviceinfo/ImsConnector.java new file mode 100644 index 0000000000..f5050be2d8 --- /dev/null +++ b/src/com/android/settings/deviceinfo/ImsConnector.java @@ -0,0 +1,106 @@ +/*
+Copyright (c) 2021 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.settings.deviceinfo;
+
+import android.content.Context;
+import android.telephony.ims.ImsMmTelManager;
+import android.util.Log;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+
+public class ImsConnector implements FeatureConnector.Listener<ImsManager> {
+ private static final String TAG = ImsConnector.class.getSimpleName();
+
+ private Context mContext;
+ private int mSlotId;
+ private FeatureConnector<ImsManager> mConnector;
+ private ImsMmTelManager.RegistrationCallback mRegistrationCallback;
+ private ImsManager mImsManager;
+
+ public ImsConnector(Context context, int slotId,
+ ImsMmTelManager.RegistrationCallback callback) {
+ mContext = context.getApplicationContext();
+ mSlotId = slotId;
+ mRegistrationCallback = callback;
+ mConnector = ImsManager.getConnector(mContext, mSlotId, TAG, this,
+ mContext.getMainExecutor());
+ }
+
+ public void connect() {
+ if (null != mConnector) {
+ mConnector.connect();
+ }
+ }
+
+ public void disconnect() {
+ if (null != mConnector) {
+ mConnector.disconnect();
+ }
+ }
+
+ @Override
+ public void connectionReady(ImsManager manager) throws ImsException {
+ mImsManager = manager;
+ registerListener();
+ }
+
+ @Override
+ public void connectionUnavailable(int reason) {
+ unregisterListener();
+ }
+
+ private void registerListener() {
+ if (null == mImsManager) {
+ Log.e(TAG, "registerListener: mImsManager is null");
+ return;
+ }
+
+ try {
+ mImsManager.addRegistrationCallback(mRegistrationCallback,
+ mContext.getMainExecutor());
+ Log.d(TAG, "registerListener: add callback for mSlotId = " + mSlotId
+ + " mImsManager = " + mImsManager);
+ } catch (ImsException e) {
+ Log.e(TAG, "registerListener: ", e);
+ }
+ }
+
+ private void unregisterListener() {
+ if (null == mImsManager) {
+ Log.e(TAG, "unregisterListener: mImsManager is null");
+ return;
+ }
+
+ mImsManager.removeRegistrationListener(mRegistrationCallback);
+ Log.d(TAG, "unregisterListener: remove ims registration callback for mSlotId = "
+ + mSlotId + " mImsManager = " + mImsManager);
+ }
+}
\ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java index 2547dbdf15..1fb31c2500 100644 --- a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java @@ -16,18 +16,23 @@ package com.android.settings.deviceinfo; -import static android.content.Context.CLIPBOARD_SERVICE; - import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.ims.ImsMmTelManager; import android.text.TextUtils; +import android.util.Log; import android.widget.Toast; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; @@ -37,9 +42,18 @@ import com.android.settings.core.BasePreferenceController; import com.android.settingslib.DeviceInfoUtils; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -public class PhoneNumberPreferenceController extends BasePreferenceController { +import static android.content.Context.CLIPBOARD_SERVICE; +import static androidx.lifecycle.Lifecycle.Event; + +public class PhoneNumberPreferenceController extends BasePreferenceController + implements LifecycleObserver { + private static final String TAG = PhoneNumberPreferenceController.class.getSimpleName(); + // This delay is used to make sure telephony framework has enough time to parse + // the phone number from the IMS registration indication message. + private static final long DELAY_MILLIS = 500L; private static final String KEY_PHONE_NUMBER = "phone_number"; private static final String KEY_PREFERENCE_CATEGORY = "basic_info_category"; @@ -47,11 +61,30 @@ public class PhoneNumberPreferenceController extends BasePreferenceController { private final TelephonyManager mTelephonyManager; private final SubscriptionManager mSubscriptionManager; private final List<Preference> mPreferenceList = new ArrayList<>(); + private HashMap<Integer, ImsConnector> mImsConnectorMap = new HashMap<>(); + private int mPhoneCount; + private Handler mHandler; + + private final ImsMmTelManager.RegistrationCallback mImsRegistrationCallback = + new ImsMmTelManager.RegistrationCallback() { + @Override + public void onRegistered(int imsTransportType) { + Log.d(TAG, "onRegistered: imsTransportType=" + imsTransportType); + if (mHandler.hasMessagesOrCallbacks()) { + Log.d(TAG, "onRegistered: optimize to remove unhandled runnables"); + mHandler.removeCallbacksAndMessages(null); + } + mHandler.postDelayed(() -> updateState(null), DELAY_MILLIS); + } + }; public PhoneNumberPreferenceController(Context context, String key) { super(context, key); mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mPhoneCount = mTelephonyManager.getPhoneCount(); + mHandler = new Handler(Looper.getMainLooper()); + initImsConnectors(); } @Override @@ -161,4 +194,67 @@ public class PhoneNumberPreferenceController extends BasePreferenceController { protected Preference createNewPreference(Context context) { return new PhoneNumberSummaryPreference(context); } + + public void init(Lifecycle lifecycle) { + if (null != lifecycle) { + lifecycle.addObserver(this); + } else { + Log.e(TAG, "init: lifecycle is null, invalid param"); + } + } + + @OnLifecycleEvent(Event.ON_RESUME) + public void onResume() { + connect(); + } + + @OnLifecycleEvent(Event.ON_PAUSE) + public void onPause() { + disconnect(); + mHandler.removeCallbacksAndMessages(null); + } + + private void initImsConnectors() { + for (int slotId = 0; slotId < mPhoneCount; slotId++) { + ImsConnector imsConnector = new ImsConnector(mContext, slotId, + mImsRegistrationCallback); + mImsConnectorMap.put(Integer.valueOf(slotId), imsConnector); + } + } + + private void connect() { + if (mImsConnectorMap.isEmpty()) { + Log.e(TAG, "connect: need init ims connectors"); + return; + } + + int size = mImsConnectorMap.size(); + for (int index = 0; index < size; index++) { + ImsConnector connector = mImsConnectorMap.get(Integer.valueOf(index)); + if (null != connector) { + connector.connect(); + } + } + } + + private void disconnect() { + if (mImsConnectorMap.isEmpty()) { + Log.d(TAG, "disconnect: need do nothing"); + return; + } + + int size = mImsConnectorMap.size(); + for (int index = 0; index < size; index++) { + ImsConnector connector = mImsConnectorMap.get(Integer.valueOf(index)); + if (null != connector) { + connector.disconnect(); + } + } + } + + @Override + protected void finalize() throws Throwable { + mImsConnectorMap.clear(); + super.finalize(); + } } diff --git a/src/com/android/settings/deviceinfo/SoftwareVersionPreferenceController.java b/src/com/android/settings/deviceinfo/SoftwareVersionPreferenceController.java new file mode 100755 index 0000000000..79482c0087 --- /dev/null +++ b/src/com/android/settings/deviceinfo/SoftwareVersionPreferenceController.java @@ -0,0 +1,73 @@ +/* +Copyright (c) 2019, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.settings.deviceinfo; + +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.core.BasePreferenceController; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +public class SoftwareVersionPreferenceController extends BasePreferenceController { + private static final String PREF_KEY = "software_version"; + + public SoftwareVersionPreferenceController(Context context) { + super(context, PREF_KEY); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Preference preference = screen.findPreference(PREF_KEY); + final CharSequence version = getSummary(); + preference.setSummary(version); + } + + @Override + public CharSequence getSummary() { + String summary = mContext.getString(R.string.device_info_default); + String softwareVersion = Utils.getString(mContext, Utils.KEY_SOFTWARE_VERSION); + if (null != softwareVersion && !softwareVersion.isEmpty()) { + summary = softwareVersion; + } + return summary; + } + + @Override + public int getAvailabilityStatus() { + return Utils.isSupportCTPA(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return PREF_KEY; + } +}
\ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/StorageSizePreferenceController.java b/src/com/android/settings/deviceinfo/StorageSizePreferenceController.java new file mode 100755 index 0000000000..36f078ddcd --- /dev/null +++ b/src/com/android/settings/deviceinfo/StorageSizePreferenceController.java @@ -0,0 +1,93 @@ +/* +Copyright (c) 2019, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.settings.deviceinfo; + +import android.content.Context; +import android.util.Log; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +public class StorageSizePreferenceController extends AbstractPreferenceController implements + PreferenceControllerMixin { + private static final String LOG_TAG = "StorageSizePreferenceController"; + private final static String KEY_STORAGE_TOTAL_SIZE = "key_storage_total_size"; + + public StorageSizePreferenceController(Context context) { + super(context); + } + + @Override + public String getPreferenceKey() { + return KEY_STORAGE_TOTAL_SIZE; + } + + @Override + public boolean isAvailable() { + return Utils.isSupportCTPA(mContext); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Preference ramSizePreference = screen.findPreference(getPreferenceKey()); + if (!isAvailable() || null == ramSizePreference || !ramSizePreference.isVisible()) { + return; + } + String ramSize = Utils.getString(mContext, Utils.KEY_RAM_TOTAL_SIZE); + Log.d(LOG_TAG, "displayPreference: ramSize = " + ramSize); + if (null != ramSize && !ramSize.isEmpty()) { + ramSizePreference.setSummary(ramSize); + } else { + ramSizePreference.setSummary(mContext.getString(R.string.device_info_default)); + } + + final Preference romSizePreference = createNewPreference(screen.getContext()); + romSizePreference.setOrder(ramSizePreference.getOrder() + 1); + romSizePreference.setKey(KEY_STORAGE_TOTAL_SIZE + 1); + screen.addPreference(romSizePreference); + romSizePreference.setVisible(true); + romSizePreference.setTitle(mContext.getResources().getString(R.string.rom_total_size)); + String romSize = Utils.getString(mContext, Utils.KEY_ROM_TOTAL_SIZE); + Log.d(LOG_TAG, "displayPreference: romSize = " + romSize); + if (null != romSize && !romSize.isEmpty()) { + romSizePreference.setSummary(romSize); + } else { + romSizePreference.setSummary(mContext.getString(R.string.device_info_default)); + } + } + + private Preference createNewPreference(Context context) { + return new Preference(context); + } +} diff --git a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java index ea4a2fd29e..10ea565d6b 100644..100755 --- a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java +++ b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java @@ -93,21 +93,24 @@ public class StorageWizardFormatProgress extends StorageWizardBase { storage.partitionPrivate(activity.mDisk.getId()); publishProgress(40); - final VolumeInfo privateVol = activity.findFirstVolume(TYPE_PRIVATE, 25); + final VolumeInfo privateVol = activity.findFirstVolume(TYPE_PRIVATE, 50); final CompletableFuture<PersistableBundle> result = new CompletableFuture<>(); - storage.benchmark(privateVol.getId(), new IVoldTaskListener.Stub() { - @Override - public void onStatus(int status, PersistableBundle extras) { - // Map benchmark 0-100% progress onto 40-80% - publishProgress(40 + ((status * 40) / 100)); - } - - @Override - public void onFinished(int status, PersistableBundle extras) { - result.complete(extras); - } - }); - mPrivateBench = result.get(60, TimeUnit.SECONDS).getLong("run", Long.MAX_VALUE); + if(null != privateVol) { + storage.benchmark(privateVol.getId(), new IVoldTaskListener.Stub() { + @Override + public void onStatus(int status, PersistableBundle extras) { + // Map benchmark 0-100% progress onto 40-80% + publishProgress(40 + ((status * 40) / 100)); + } + + @Override + public void onFinished(int status, PersistableBundle extras) { + result.complete(extras); + } + }); + mPrivateBench = result.get(60, TimeUnit.SECONDS).getLong("run", + Long.MAX_VALUE); + } // If we just adopted the device that had been providing // physical storage, then automatically move storage to the diff --git a/src/com/android/settings/deviceinfo/WifiMacAddressPreferenceController.java b/src/com/android/settings/deviceinfo/WifiMacAddressPreferenceController.java index c7005966c4..8b0b4ec4c1 100644 --- a/src/com/android/settings/deviceinfo/WifiMacAddressPreferenceController.java +++ b/src/com/android/settings/deviceinfo/WifiMacAddressPreferenceController.java @@ -17,11 +17,16 @@ package com.android.settings.deviceinfo; import android.content.Context; +import android.net.wifi.WifiInfo; +import android.util.Log; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.deviceinfo.AbstractWifiMacAddressPreferenceController; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; /** * Concrete subclass of WIFI MAC address preference controller @@ -37,5 +42,25 @@ public class WifiMacAddressPreferenceController extends AbstractWifiMacAddressPr return mContext.getResources().getBoolean(R.bool.config_show_wifi_mac_address); } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + if (Utils.isSupportCTPA(mContext)) { + Preference macAddressPreference = screen.findPreference(getPreferenceKey()); + CharSequence oldValue = macAddressPreference.getSummary(); + String macAddress = Utils.getString(mContext, Utils.KEY_WIFI_MAC_ADDRESS); + String unAvailable = mContext.getString( + com.android.settingslib.R.string.status_unavailable); + Log.d(TAG, "displayPreference: macAddress = " + macAddress + + " oldValue = " + oldValue + " unAvailable = " + unAvailable); + if (null == macAddress || macAddress.isEmpty()) { + macAddress = unAvailable; + } + if (null != oldValue && (WifiInfo.DEFAULT_MAC_ADDRESS.equals(oldValue) || + unAvailable.equals(oldValue))) { + macAddressPreference.setSummary(macAddress); + } + } + } // This space intentionally left blank } diff --git a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java index 4af5d79c37..dcfb0e8efe 100644 --- a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java +++ b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserManager; +import android.util.Log; import android.view.View; import com.android.settings.R; @@ -35,8 +36,11 @@ import com.android.settings.deviceinfo.FccEquipmentIdPreferenceController; import com.android.settings.deviceinfo.FeedbackPreferenceController; import com.android.settings.deviceinfo.IpAddressPreferenceController; import com.android.settings.deviceinfo.ManualPreferenceController; +import com.android.settings.deviceinfo.PhoneNumberPreferenceController; import com.android.settings.deviceinfo.RegulatoryInfoPreferenceController; import com.android.settings.deviceinfo.SafetyInfoPreferenceController; +import com.android.settings.deviceinfo.SoftwareVersionPreferenceController; +import com.android.settings.deviceinfo.StorageSizePreferenceController; import com.android.settings.deviceinfo.UptimePreferenceController; import com.android.settings.deviceinfo.WifiMacAddressPreferenceController; import com.android.settings.deviceinfo.imei.ImeiInfoPreferenceController; @@ -51,6 +55,12 @@ import com.android.settingslib.widget.LayoutPreference; import java.util.ArrayList; import java.util.List; +import android.content.BroadcastReceiver; +import android.content.IntentFilter; + +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; + @SearchIndexable public class MyDeviceInfoFragment extends DashboardFragment implements DeviceNamePreferenceController.DeviceNamePreferenceHost { @@ -58,6 +68,17 @@ public class MyDeviceInfoFragment extends DashboardFragment private static final String LOG_TAG = "MyDeviceInfoFragment"; private static final String KEY_MY_DEVICE_INFO_HEADER = "my_device_info_header"; + private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { + String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + Log.d(LOG_TAG, "Received ACTION_SIM_STATE_CHANGED: " + state); + updatePreferenceStates(); + } + } + }; + private BuildNumberPreferenceController mBuildNumberPreferenceController; @Override @@ -77,6 +98,7 @@ public class MyDeviceInfoFragment extends DashboardFragment use(DeviceNamePreferenceController.class).setHost(this /* parent */); mBuildNumberPreferenceController = use(BuildNumberPreferenceController.class); mBuildNumberPreferenceController.setHost(this /* parent */); + use(PhoneNumberPreferenceController.class).init(getSettingsLifecycle()); } @Override @@ -86,6 +108,29 @@ public class MyDeviceInfoFragment extends DashboardFragment } @Override + public void onPause() { + super.onPause(); + Context context = getContext(); + if (context != null) { + context.unregisterReceiver(mSimStateReceiver); + } else { + Log.i(LOG_TAG, "context already null, not unregistering SimStateReceiver"); + } + } + + @Override + public void onResume() { + super.onResume(); + Context context = getContext(); + if (context != null) { + context.registerReceiver(mSimStateReceiver, + new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED)); + } else { + Log.i(LOG_TAG, "context is null, not registering SimStateReceiver"); + } + } + + @Override protected String getLogTag() { return LOG_TAG; } @@ -113,6 +158,8 @@ public class MyDeviceInfoFragment extends DashboardFragment controllers.add(new FeedbackPreferenceController(fragment, context)); controllers.add(new FccEquipmentIdPreferenceController(context)); controllers.add(new UptimePreferenceController(context, lifecycle)); + controllers.add(new SoftwareVersionPreferenceController(context)); + controllers.add(new StorageSizePreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java index dd3d560282..1507ddc80b 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java +++ b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java @@ -29,9 +29,11 @@ public class BasebandVersionPreferenceController extends BasePreferenceControlle @VisibleForTesting static final String BASEBAND_PROPERTY = "gsm.version.baseband"; + private final Context mContext; public BasebandVersionPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); + mContext = context; } @Override @@ -41,6 +43,16 @@ public class BasebandVersionPreferenceController extends BasePreferenceControlle @Override public CharSequence getSummary() { + if (Utils.isSupportCTPA(mContext.getApplicationContext())) { + String baseBands = SystemProperties.get(BASEBAND_PROPERTY, + mContext.getString(R.string.device_info_default)); + if (null != baseBands) { + String[] baseBandArray = baseBands.split(","); + if ((baseBandArray != null) && (baseBandArray.length > 0)) { + return baseBandArray[0]; + } + } + } return SystemProperties.get(BASEBAND_PROPERTY, mContext.getString(R.string.device_info_default)); } diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java index e5fd3daa94..9d00b6667e 100644 --- a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java +++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java @@ -21,6 +21,7 @@ import android.os.SystemProperties; import android.text.TextUtils; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.slices.Sliceable; @@ -49,6 +50,12 @@ public class HardwareRevisionPreferenceController extends BasePreferenceControll @Override public CharSequence getSummary() { + if (Utils.isSupportCTPA(mContext)) { + String hardwareVersion = Utils.getString(mContext, Utils.KEY_HARDWARE_VERSION); + if (null != hardwareVersion && !hardwareVersion.isEmpty()) { + return hardwareVersion; + } + } return SystemProperties.get("ro.boot.hardware.revision"); } } diff --git a/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java b/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java index 1ae6b4007b..db93f2a2b7 100644 --- a/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java +++ b/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java @@ -21,12 +21,16 @@ import android.content.res.Resources; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.settings.R; +import com.android.settings.network.telephony.TelephonyUtils; + +import com.qti.extphone.QtiImeiInfo; public class ImeiInfoDialogController { @@ -52,6 +56,7 @@ public class ImeiInfoDialogController { private final TelephonyManager mTelephonyManager; private final SubscriptionInfo mSubscriptionInfo; private final int mSlotId; + private QtiImeiInfo mQtiImeiInfo[]; public ImeiInfoDialogController(@NonNull ImeiInfoDialogFragment dialog, int slotId) { mDialog = dialog; @@ -68,6 +73,23 @@ public class ImeiInfoDialogController { } else { mTelephonyManager = null; } + mQtiImeiInfo = TelephonyUtils.getImeiInfo(); + } + + private String getImei(int slot) { + String imei = null; + if (mQtiImeiInfo != null) { + for (int i = 0; i < mQtiImeiInfo.length; i++) { + if (mQtiImeiInfo[i].getSlotId() == slot) { + imei = mQtiImeiInfo[i].getImei(); + break; + } + } + } + if (TextUtils.isEmpty(imei)) { + imei = mTelephonyManager.getImei(slot); + } + return imei; } /** @@ -103,7 +125,7 @@ public class ImeiInfoDialogController { if ((mSubscriptionInfo != null && isCdmaLteEnabled()) || (mSubscriptionInfo == null && isSimPresent(mSlotId))) { // Show IMEI for LTE device - mDialog.setText(ID_IMEI_VALUE, mTelephonyManager.getImei(mSlotId)); + mDialog.setText(ID_IMEI_VALUE, getImei(mSlotId)); mDialog.setText(ID_IMEI_SV_VALUE, mTelephonyManager.getDeviceSoftwareVersion(mSlotId)); } else { @@ -113,7 +135,7 @@ public class ImeiInfoDialogController { } private void updateDialogForGsmPhone() { - mDialog.setText(ID_IMEI_VALUE, mTelephonyManager.getImei(mSlotId)); + mDialog.setText(ID_IMEI_VALUE, getImei(mSlotId)); mDialog.setText(ID_IMEI_SV_VALUE, mTelephonyManager.getDeviceSoftwareVersion(mSlotId)); // device is not CDMA, do not display CDMA features diff --git a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java index e0bff6d51a..f45640fe02 100644 --- a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java +++ b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java @@ -23,6 +23,8 @@ import android.os.UserManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; @@ -33,8 +35,11 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.deviceinfo.PhoneNumberSummaryPreference; +import com.android.settings.network.telephony.TelephonyUtils; import com.android.settings.slices.Sliceable; -import com.android.settingslib.Utils; +import com.android.settings.Utils; + +import com.qti.extphone.QtiImeiInfo; import java.util.ArrayList; import java.util.List; @@ -45,16 +50,21 @@ import java.util.List; public class ImeiInfoPreferenceController extends BasePreferenceController { private static final String KEY_PREFERENCE_CATEGORY = "device_detail_category"; + private static final String TAG = "ImeiInfoPreferenceController"; private final boolean mIsMultiSim; private final TelephonyManager mTelephonyManager; private final List<Preference> mPreferenceList = new ArrayList<>(); private Fragment mFragment; + private QtiImeiInfo mQtiImeiInfo[]; public ImeiInfoPreferenceController(Context context, String key) { super(context, key); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mIsMultiSim = mTelephonyManager.getPhoneCount() > 1; + if (mIsMultiSim) { + TelephonyUtils.connectExtTelephonyService(context); + } } public void setHost(Fragment fragment) { @@ -81,6 +91,54 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { mPreferenceList.add(multiSimPreference); updatePreference(multiSimPreference, simSlotNumber); } + + final int phoneCount = mTelephonyManager.getPhoneCount(); + if (Utils.isSupportCTPA(mContext) && phoneCount >= 2) { + final int slot0PhoneType = mTelephonyManager.getCurrentPhoneTypeForSlot(0); + final int slot1PhoneType = mTelephonyManager.getCurrentPhoneTypeForSlot(1); + if (PHONE_TYPE_CDMA != slot0PhoneType && PHONE_TYPE_CDMA != slot1PhoneType) { + addPreferenceNotInList(screen, 0, imeiPreferenceOrder + phoneCount, + getPreferenceKey() + phoneCount, true); + } else if (PHONE_TYPE_CDMA == slot0PhoneType){ + addPreferenceNotInList(screen, 0, imeiPreferenceOrder + phoneCount, + getPreferenceKey() + phoneCount, false); + } else if (PHONE_TYPE_CDMA == slot1PhoneType) { + addPreferenceNotInList(screen, 1, imeiPreferenceOrder + phoneCount, + getPreferenceKey() + phoneCount, false); + } + } + } + + private void addPreferenceNotInList(PreferenceScreen screen, int slotNumber, int order, + String key, boolean isCDMAPhone) { + final Preference multiSimPreference = createNewPreference(screen.getContext()); + multiSimPreference.setOrder(order); + multiSimPreference.setKey(key); + final PreferenceCategory category = screen.findPreference(KEY_PREFERENCE_CATEGORY); + category.addPreference(multiSimPreference); + if (isCDMAPhone) { + multiSimPreference.setTitle(getTitleForCdmaPhone(slotNumber)); + multiSimPreference.setSummary(mTelephonyManager.getMeid(slotNumber)); + } else { + multiSimPreference.setTitle(getTitleForGsmPhone(slotNumber)); + multiSimPreference.setSummary(getImei(slotNumber)); + } + } + + private void addPreference(PreferenceScreen screen, int slotNumber, int order, + String key, boolean isCDMAPhone) { + final Preference multiSimPreference = createNewPreference(screen.getContext()); + multiSimPreference.setOrder(order); + multiSimPreference.setKey(key); + screen.addPreference(multiSimPreference); + mPreferenceList.add(multiSimPreference); + if (isCDMAPhone) { + multiSimPreference.setTitle(getTitleForCdmaPhone(slotNumber)); + multiSimPreference.setSummary(mTelephonyManager.getMeid(slotNumber)); + } else { + multiSimPreference.setTitle(getTitleForGsmPhone(slotNumber)); + multiSimPreference.setSummary(getImei(slotNumber)); + } } @Override @@ -88,6 +146,8 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { if (preference == null) { return; } + Log.d(TAG, "updateState"); + mQtiImeiInfo = TelephonyUtils.getImeiInfo(); int size = mPreferenceList.size(); for (int i = 0; i < size; i++) { Preference pref = mPreferenceList.get(i); @@ -102,8 +162,14 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { private CharSequence getSummary(int simSlot) { final int phoneType = getPhoneType(simSlot); + if (Utils.isSupportCTPA(mContext)) { + // only can obtain the MEID by slot 0 + if (PHONE_TYPE_CDMA == phoneType) { + simSlot = 0; + } + } return phoneType == PHONE_TYPE_CDMA ? mTelephonyManager.getMeid(simSlot) - : mTelephonyManager.getImei(simSlot); + : getImei(simSlot); } @Override @@ -113,6 +179,10 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { return false; } + if (Utils.isSupportCTPA(mContext)) { + return true; + } + ImeiInfoDialogFragment.show(mFragment, simSlot, preference.getTitle().toString()); return true; } @@ -138,9 +208,49 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { preference.setSummary(getSummary(simSlot)); } + private boolean isPrimaryImeiSlot(int slot) { + boolean primaryImeiSlotStatus = false; + if (mQtiImeiInfo == null) { + mQtiImeiInfo = TelephonyUtils.getImeiInfo(); + } + if (mQtiImeiInfo != null) { + for (int i = 0; i < mQtiImeiInfo.length; i++) { + if (null != mQtiImeiInfo[i] && mQtiImeiInfo[i].getSlotId() == slot && + mQtiImeiInfo[i].getImeiType() == QtiImeiInfo.IMEI_TYPE_PRIMARY) { + primaryImeiSlotStatus = true; + break; + } + } + } + return primaryImeiSlotStatus; + } + + private String getImei(int slot) { + String imei = null; + if (mQtiImeiInfo == null) { + mQtiImeiInfo = TelephonyUtils.getImeiInfo(); + } + if (mQtiImeiInfo != null) { + for (int i = 0; i < mQtiImeiInfo.length; i++) { + if (mQtiImeiInfo[i].getSlotId() == slot) { + imei = mQtiImeiInfo[i].getImei(); + break; + } + } + } + if (TextUtils.isEmpty(imei)) { + imei = mTelephonyManager.getImei(slot); + } + return imei; + } + private CharSequence getTitleForGsmPhone(int simSlot) { - return mIsMultiSim ? mContext.getString(R.string.imei_multi_sim, simSlot + 1) + CharSequence title = mIsMultiSim ? mContext.getString(R.string.imei_multi_sim, simSlot + 1) : mContext.getString(R.string.status_imei); + if (mIsMultiSim && isPrimaryImeiSlot(simSlot)) { + title += " (Primary)"; + } + return title; } private CharSequence getTitleForCdmaPhone(int simSlot) { @@ -155,6 +265,9 @@ public class ImeiInfoPreferenceController extends BasePreferenceController { } private int getPhoneType(int slotIndex) { + if (Utils.isSupportCTPA(mContext)) { + return mTelephonyManager.getCurrentPhoneTypeForSlot(slotIndex); + } SubscriptionInfo subInfo = SubscriptionManager.from(mContext) .getActiveSubscriptionInfoForSimSlotIndex(slotIndex); return mTelephonyManager.getCurrentPhoneType(subInfo != null ? subInfo.getSubscriptionId() diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java index de4fe25bc9..f949614d1a 100644..100755 --- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java +++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java @@ -104,8 +104,8 @@ public class BrightnessLevelPreferenceController extends AbstractPreferenceContr if (lifecycle != null) { lifecycle.addObserver(this); } - final PowerManager powerManager = context.getSystemService(PowerManager.class); + final PowerManager powerManager = context.getSystemService(PowerManager.class); mMinVrBrightness = powerManager.getBrightnessConstraint( PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); mMaxVrBrightness = powerManager.getBrightnessConstraint( diff --git a/src/com/android/settings/location/AgpsPreferenceController.java b/src/com/android/settings/location/AgpsPreferenceController.java new file mode 100755 index 0000000000..1504668faa --- /dev/null +++ b/src/com/android/settings/location/AgpsPreferenceController.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 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.location; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.CheckBoxPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; + + +public class AgpsPreferenceController extends LocationBasePreferenceController { + private static final String KEY_ASSISTED_GPS = "assisted_gps"; + + private CheckBoxPreference mAgpsPreference; + + public AgpsPreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public String getPreferenceKey() { + return KEY_ASSISTED_GPS; + } + + @AvailabilityStatus + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean(R.bool.config_agps_enabled) + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mAgpsPreference = + (CheckBoxPreference) screen.findPreference(KEY_ASSISTED_GPS); + } + + @Override + public void updateState(Preference preference) { + if (mAgpsPreference != null) { + mAgpsPreference.setChecked(Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 0) == 1); + } + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (KEY_ASSISTED_GPS.equals(preference.getKey())) { + final ContentResolver cr = mContext.getContentResolver(); + final boolean switchState = mAgpsPreference.isChecked(); + Settings.Global.putInt(cr, Settings.Global.ASSISTED_GPS_ENABLED, + switchState ? 1 : 0); + return true; + } + return false; + } + + @Override + public void onLocationModeChanged(int mode, boolean restricted) {} +} diff --git a/src/com/android/settings/location/AppSettingsInjector.java b/src/com/android/settings/location/AppSettingsInjector.java index 7bea99931e..899d4ef9cb 100644 --- a/src/com/android/settings/location/AppSettingsInjector.java +++ b/src/com/android/settings/location/AppSettingsInjector.java @@ -19,9 +19,19 @@ package com.android.settings.location; import android.content.Context; import android.content.Intent; import android.text.TextUtils; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.UserHandle; + +import android.location.SettingInjectorService; import androidx.preference.Preference; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParserException; + import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.RestrictedAppPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -43,11 +53,30 @@ public class AppSettingsInjector extends SettingsInjector { mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } + /** + * Returns the settings parsed from the attributes of the + * {@link SettingInjectorService#META_DATA_NAME} tag, or null. + * + * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}. + */ + @Override + protected InjectedSetting parseServiceInfo(ResolveInfo service, UserHandle userHandle, + PackageManager pm) throws XmlPullParserException, IOException { + InjectedSetting res = super.parseServiceInfo(service, userHandle, pm); + ServiceInfo si = service.serviceInfo; + + if ((null != res) && (!DimmableIZatIconPreference.showIzat(mContext, si.packageName))) { + res = null; + } + + return res; + } + @Override protected Preference createPreference(Context prefContext, InjectedSetting setting) { return TextUtils.isEmpty(setting.userRestriction) - ? new AppPreference(prefContext) - : new RestrictedAppPreference(prefContext, setting.userRestriction); + ? DimmableIZatIconPreference.getAppPreference(prefContext, setting) + : DimmableIZatIconPreference.getRestrictedAppPreference(prefContext, setting); } @Override diff --git a/src/com/android/settings/location/DimmableIZatIconPreference.java b/src/com/android/settings/location/DimmableIZatIconPreference.java new file mode 100644 index 0000000000..53723e663b --- /dev/null +++ b/src/com/android/settings/location/DimmableIZatIconPreference.java @@ -0,0 +1,227 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package com.android.settings.location; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.location.InjectedSetting; +import android.util.Log; +import com.android.settingslib.widget.AppPreference; +import com.android.settings.widget.RestrictedAppPreference; +import dalvik.system.DexClassLoader; +import java.lang.ClassNotFoundException; +import java.lang.ExceptionInInitializerError; +import java.lang.IllegalAccessException; +import java.lang.IllegalArgumentException; +import java.lang.LinkageError; +import java.lang.NoSuchFieldException; +import java.lang.NoSuchMethodException; +import java.lang.NullPointerException; +import java.lang.SecurityException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import com.android.settings.R; + +public class DimmableIZatIconPreference { + private static final String TAG = "DimmableIZatIconPreference"; + private static Class mXtProxyClz; + private static Class mNotifierClz; + private static Method mGetXtProxyMethod; + private static Method mGetConsentMethod; + private static Method mShowIzatMethod; + private static String mIzatPackage; + private static DexClassLoader mLoader; + + private static void load(Context context) { + if (mLoader == null) { + try { + if (mXtProxyClz == null || mNotifierClz == null) { + mLoader = new DexClassLoader("/system_ext/framework/izat.xt.srv.jar", + context.getFilesDir().getAbsolutePath(), + null, + ClassLoader.getSystemClassLoader()); + mXtProxyClz = Class.forName("com.qti.izat.XTProxy", + true, + mLoader); + mNotifierClz = Class.forName("com.qti.izat.XTProxy$Notifier", + true, + mLoader); + mIzatPackage = (String)mXtProxyClz.getField("IZAT_XT_PACKAGE").get(null); + mGetXtProxyMethod = mXtProxyClz.getMethod("getXTProxy", + Context.class, + mNotifierClz); + mGetConsentMethod = mXtProxyClz.getMethod("getUserConsent"); + mShowIzatMethod = mXtProxyClz.getMethod("showIzat", + Context.class, + String.class); + } + } catch (NoSuchMethodException | NullPointerException | SecurityException | + NoSuchFieldException | LinkageError | IllegalAccessException | + ClassNotFoundException e) { + mXtProxyClz = null; + mNotifierClz = null; + mIzatPackage = null; + mGetXtProxyMethod = null; + mGetConsentMethod = null; + mShowIzatMethod = null; + e.printStackTrace(); + } + } + } + + static boolean showIzat(Context context, String packageName) { + load(context); + boolean show = true; + try { + if (mShowIzatMethod != null) { + show = (Boolean)mShowIzatMethod.invoke(null, context, packageName); + } + } catch (IllegalAccessException | IllegalArgumentException | + InvocationTargetException | ExceptionInInitializerError e) { + e.printStackTrace(); + } + return show; + } + + private static boolean isIzatPackage(Context context, InjectedSetting info) { + return (mIzatPackage != null && mIzatPackage.equals(info.packageName)); + } + + private static final int ICON_ALPHA_ENABLED = 255; + private static final int ICON_ALPHA_DISABLED = 102; + + private static void dimIcon(AppPreference pref, boolean dimmed) { + Drawable icon = pref.getIcon(); + if (icon != null) { + icon.mutate().setAlpha(dimmed ? ICON_ALPHA_DISABLED : ICON_ALPHA_ENABLED); + pref.setIcon(icon); + } + } + + private static class IZatAppPreference extends AppPreference { + private boolean mChecked; + private Context mContext; + private IZatAppPreference(Context context) { + super(context); + mContext = context; + Object notifier = Proxy.newProxyInstance(mLoader, + new Class[] { mNotifierClz }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (method.getName().equals("userConsentNotify") && + args[0] != null && args[0] instanceof Boolean) { + boolean consent = (Boolean)args[0]; + if (mChecked != consent) { + mChecked = consent; + dimIcon(IZatAppPreference.this, !isEnabled() || !mChecked); + } + } + return null; + }}); + + try { + Object xt = mGetXtProxyMethod.invoke(null, context, notifier); + mChecked = (Boolean)mGetConsentMethod.invoke(xt); + } catch (IllegalAccessException | IllegalArgumentException | + InvocationTargetException | ExceptionInInitializerError e) { + e.printStackTrace(); + } + } + + @Override + public CharSequence getSummary() { + int resId; + if (!isEnabled() || !mChecked) { + resId = R.string.notification_toggle_off; + } else { + resId = R.string.notification_toggle_on; + } + return mContext.getString(resId); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + DimmableIZatIconPreference.dimIcon(this, !isEnabled() || !mChecked); + } + } + + private static class IZatRestrictedAppPreference extends RestrictedAppPreference { + private boolean mChecked; + private IZatRestrictedAppPreference(Context context, String userRestriction) { + super(context, userRestriction); + Object notifier = Proxy.newProxyInstance(mLoader, + new Class[] { mNotifierClz }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (method.getName().equals("userConsentNotify") && + args[0] != null && args[0] instanceof Boolean) { + boolean consent = (Boolean)args[0]; + if (mChecked != consent) { + mChecked = consent; + dimIcon(IZatRestrictedAppPreference.this, !isEnabled() || !mChecked); + } + } + return null; + }}); + + try { + Object xt = mGetXtProxyMethod.invoke(null, context, notifier); + mChecked = (Boolean)mGetConsentMethod.invoke(xt); + } catch (IllegalAccessException | IllegalArgumentException | + InvocationTargetException | ExceptionInInitializerError e) { + e.printStackTrace(); + } + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + DimmableIZatIconPreference.dimIcon(this, !isEnabled() || !mChecked); + } + } + + static AppPreference getAppPreference(Context context, InjectedSetting info) { + return isIzatPackage(context, info) ? + new IZatAppPreference(context) : + new AppPreference(context); + } + + static RestrictedAppPreference getRestrictedAppPreference(Context context, InjectedSetting info) { + return isIzatPackage(context, info) ? + new IZatRestrictedAppPreference(context, info.userRestriction) : + new RestrictedAppPreference(context, info.userRestriction); + } +} diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java index 8f9787b08a..62685d892c 100644 --- a/src/com/android/settings/location/LocationSettings.java +++ b/src/com/android/settings/location/LocationSettings.java @@ -90,6 +90,7 @@ public class LocationSettings extends DashboardFragment implements use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this); use(LocationForWorkPreferenceController.class).init(this); use(LocationSettingsFooterPreferenceController.class).init(this); + use(AgpsPreferenceController.class).init(this); } @Override diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java index e74f3ae979..a2c56b53b7 100644 --- a/src/com/android/settings/network/AirplaneModePreferenceController.java +++ b/src/com/android/settings/network/AirplaneModePreferenceController.java @@ -17,12 +17,17 @@ package com.android.settings.network; import static android.provider.SettingsSlicesContract.KEY_AIRPLANE_MODE; +import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.provider.SettingsSlicesContract; +import android.provider.Settings; import android.telephony.TelephonyManager; import androidx.annotation.VisibleForTesting; @@ -34,16 +39,22 @@ import androidx.preference.SwitchPreference; import com.android.settings.AirplaneModeEnabler; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; +import com.android.settings.slices.SliceBackgroundWorker; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnDestroy; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import com.qti.extphone.ExtTelephonyManager; + +import java.io.IOException; + public class AirplaneModePreferenceController extends TogglePreferenceController implements LifecycleObserver, OnStart, OnStop, OnDestroy, AirplaneModeEnabler.OnAirplaneModeChangedListener { public static final int REQUEST_CODE_EXIT_ECM = 1; + public static final int REQUEST_CODE_EXIT_SCBM = 2; /** * Uri for Airplane mode Slice. @@ -79,17 +90,25 @@ public class AirplaneModePreferenceController extends TogglePreferenceController @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (KEY_AIRPLANE_MODE.equals(preference.getKey()) - && mAirplaneModeEnabler.isInEcmMode()) { - // In ECM mode launch ECM app dialog - if (mFragment != null) { - mFragment.startActivityForResult( - new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), - REQUEST_CODE_EXIT_ECM); + if (KEY_AIRPLANE_MODE.equals(preference.getKey())) { + if(mAirplaneModeEnabler.isInEcmMode()) { + // In ECM mode launch ECM app dialog + if (mFragment != null) { + mFragment.startActivityForResult( + new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), + REQUEST_CODE_EXIT_ECM); + } + return true; + } else if(mAirplaneModeEnabler.isInScbm()) { + // In SCBM mode launch SCBM app dialog + if (mFragment != null) { + mFragment.startActivityForResult( + new Intent(ExtTelephonyManager.ACTION_SHOW_NOTICE_SCM_BLOCK_OTHERS, + null), REQUEST_CODE_EXIT_SCBM); + } + return true; } - return true; } - return false; } @@ -149,7 +168,12 @@ public class AirplaneModePreferenceController extends TogglePreferenceController if (requestCode == REQUEST_CODE_EXIT_ECM) { final boolean isChoiceYes = data.getBooleanExtra(EXIT_ECM_RESULT, false); // Set Airplane mode based on the return value and checkbox state - mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes, + mAirplaneModeEnabler.setAirplaneModeInEmergencyMode(isChoiceYes, + mAirplaneModePreference.isChecked()); + } else if (requestCode == REQUEST_CODE_EXIT_SCBM) { + final boolean isChoiceYes = resultCode == Activity.RESULT_OK; + // Set Airplane mode based on the return value and checkbox state + mAirplaneModeEnabler.setAirplaneModeInEmergencyMode(isChoiceYes, mAirplaneModePreference.isChecked()); } } @@ -174,4 +198,72 @@ public class AirplaneModePreferenceController extends TogglePreferenceController mAirplaneModePreference.setChecked(isAirplaneModeOn); } } + + /** + * According to slice framework, need override this function and provide background + * worker class to support slice's dynamic update. + */ + @Override + public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() { + return AirplaneModeSliceWorker.class; + } + + /** + * Register content observer for URI Settings.Global.AIRPLANE_MODE_ON. + * If changed, notify airplane mode slice do rebind. + */ + public static class AirplaneModeSliceWorker extends SliceBackgroundWorker<Void> { + private AirplaneModeContentObserver mContentObserver; + + public AirplaneModeSliceWorker(Context context, Uri uri) { + super(context, uri); + final Handler handler = new Handler(Looper.getMainLooper()); + mContentObserver = new AirplaneModeContentObserver(handler, this); + } + + @Override + protected void onSlicePinned() { + mContentObserver.register(getContext()); + } + + @Override + protected void onSliceUnpinned() { + mContentObserver.unRegister(getContext()); + } + + @Override + public void close() throws IOException { + mContentObserver = null; + } + + public void updateSlice() { + notifySliceChange(); + } + + public class AirplaneModeContentObserver extends ContentObserver { + private final AirplaneModeSliceWorker mSliceBackgroundWorker; + + public AirplaneModeContentObserver(Handler handler, + AirplaneModeSliceWorker backgroundWorker) { + super(handler); + mSliceBackgroundWorker = backgroundWorker; + } + + @Override + public void onChange(boolean selfChange) { + mSliceBackgroundWorker.updateSlice(); + } + + public void register(Context context) { + final Uri airplaneModeUri = Settings.Global.getUriFor( + Settings.Global.AIRPLANE_MODE_ON); + context.getContentResolver().registerContentObserver(airplaneModeUri, + false, this); + } + + public void unRegister(Context context) { + context.getContentResolver().unregisterContentObserver(this); + } + } + } } diff --git a/src/com/android/settings/network/MobileNetworkListController.java b/src/com/android/settings/network/MobileNetworkListController.java index d7fc8b4d58..0d0248eaeb 100644 --- a/src/com/android/settings/network/MobileNetworkListController.java +++ b/src/com/android/settings/network/MobileNetworkListController.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import androidx.lifecycle.Lifecycle; @@ -122,7 +123,10 @@ public class MobileNetworkListController extends AbstractPreferenceController im pref.setSummary(R.string.mobile_network_inactive_esim); } } else { - if (mSubscriptionManager.isActiveSubscriptionId(subId)) { + int slotId = mSubscriptionManager.getPhoneId(subId); + if (mSubscriptionManager.isActiveSubscriptionId(subId) && + mSubscriptionManager.getSimStateForSlotIndex(slotId) != + TelephonyManager.SIM_STATE_NOT_READY) { pref.setSummary(R.string.mobile_network_active_sim); } else if (SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) { pref.setSummary(mContext.getString(R.string.mobile_network_inactive_sim)); diff --git a/src/com/android/settings/network/MobileNetworkPreferenceController.java b/src/com/android/settings/network/MobileNetworkPreferenceController.java index 527a632d70..c9d4aa6be8 100644 --- a/src/com/android/settings/network/MobileNetworkPreferenceController.java +++ b/src/com/android/settings/network/MobileNetworkPreferenceController.java @@ -21,6 +21,7 @@ import static android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS; import static androidx.lifecycle.Lifecycle.Event; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -28,6 +29,8 @@ import android.os.UserManager; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; @@ -42,9 +45,11 @@ import com.android.settings.network.telephony.MobileNetworkActivity; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; -import com.android.settingslib.Utils; +import com.android.settings.Utils; import com.android.settingslib.core.AbstractPreferenceController; +import java.util.List; + public class MobileNetworkPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver { @@ -57,9 +62,12 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl private Preference mPreference; @VisibleForTesting MobileNetworkTelephonyCallback mTelephonyCallback; + private SubscriptionManager mSubscriptionManager; private BroadcastReceiver mAirplanModeChangedReceiver; + private String mSummary; + public MobileNetworkPreferenceController(Context context) { super(context); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); @@ -69,9 +77,11 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl mAirplanModeChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + updateDisplayName(); updateState(mPreference); } }; + mSubscriptionManager = SubscriptionManager.from(context); } @Override @@ -102,12 +112,15 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl TelephonyCallback.ServiceStateListener { @Override public void onServiceStateChanged(ServiceState serviceState) { + updateDisplayName(); updateState(mPreference); } } @OnLifecycleEvent(Event.ON_START) public void onStart() { + if (mSubscriptionManager != null) + mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); if (isAvailable()) { if (mTelephonyCallback == null) { mTelephonyCallback = new MobileNetworkTelephonyCallback(); @@ -121,11 +134,53 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl } } + private void updateDisplayName() { + if (mPreference != null) { + List<SubscriptionInfo> list = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (list != null && !list.isEmpty()) { + boolean useSeparator = false; + StringBuilder builder = new StringBuilder(); + for (SubscriptionInfo subInfo : list) { + if (isSubscriptionInService(subInfo.getSubscriptionId())) { + if (useSeparator) builder.append(", "); + builder.append(mTelephonyManager.getNetworkOperatorName + (subInfo.getSubscriptionId())); + useSeparator = true; + } + } + mSummary = builder.toString(); + } else { + mSummary = mTelephonyManager.getNetworkOperatorName(); + } + } + } + + private boolean isSubscriptionInService(int subId) { + if (mTelephonyManager != null) { + if (mTelephonyManager.getServiceStateForSubscriber(subId).getState() + == ServiceState.STATE_IN_SERVICE) { + return true; + } + } + return false; + } + + private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener + = new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + updateDisplayName(); + updateState(mPreference); + } + }; + @OnLifecycleEvent(Event.ON_STOP) public void onStop() { if (mTelephonyCallback != null) { mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); } + mSubscriptionManager + .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); if (mAirplanModeChangedReceiver != null) { mContext.unregisterReceiver(mAirplanModeChangedReceiver); } @@ -155,6 +210,6 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl @Override public CharSequence getSummary() { - return MobileNetworkUtils.getCurrentCarrierNameForDisplay(mContext); + return mSummary; } } diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java index 286e4e36e5..06dd92f3b6 100644 --- a/src/com/android/settings/network/NetworkDashboardFragment.java +++ b/src/com/android/settings/network/NetworkDashboardFragment.java @@ -20,6 +20,7 @@ import static com.android.settings.network.MobilePlanPreferenceController.MANAGE import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.provider.SearchIndexableResource; import android.util.Log; @@ -31,6 +32,7 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost; +import com.android.settings.network.telephony.TelephonyUtils; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.wifi.WifiPrimarySwitchPreferenceController; import com.android.settingslib.core.AbstractPreferenceController; @@ -72,6 +74,12 @@ public class NetworkDashboardFragment extends DashboardFragment implements } @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + use(AirplaneModePreferenceController.class).onActivityResult(requestCode, resultCode, data); + super.onActivityResult(requestCode, resultCode, data); + } + + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); use(AllInOneTetherPreferenceController.class).initEnabler(getSettingsLifecycle()); @@ -91,6 +99,8 @@ public class NetworkDashboardFragment extends DashboardFragment implements private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, Lifecycle lifecycle, MetricsFeatureProvider metricsFeatureProvider, Fragment fragment, MobilePlanPreferenceHost mobilePlanHost) { + // Connect to ExtTelephonyService + TelephonyUtils.connectExtTelephonyService(context); final MobilePlanPreferenceController mobilePlanPreferenceController = new MobilePlanPreferenceController(context, mobilePlanHost); final InternetPreferenceController internetPreferenceController = diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java index 32a475a7d0..d264fd69d3 100644 --- a/src/com/android/settings/network/ProviderModelSliceHelper.java +++ b/src/com/android/settings/network/ProviderModelSliceHelper.java @@ -140,7 +140,7 @@ public class ProviderModelSliceHelper { numLevels += 1; } return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, - NO_CELL_DATA_TYPE_ICON, false); + NO_CELL_DATA_TYPE_ICON, !mTelephonyManager.isDataEnabled()); } /** diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index 2d6077868d..7222b6d2b7 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -368,7 +368,7 @@ public class SubscriptionUtil { * Whether Settings should show a "Use SIM" toggle in pSIM detailed page. */ public static boolean showToggleForPhysicalSim(SubscriptionManager subMgr) { - return subMgr.canDisablePhysicalSubscription(); + return true; } /** diff --git a/src/com/android/settings/network/SubscriptionsChangeListener.java b/src/com/android/settings/network/SubscriptionsChangeListener.java index e13f85c5ab..2ee22af638 100644 --- a/src/com/android/settings/network/SubscriptionsChangeListener.java +++ b/src/com/android/settings/network/SubscriptionsChangeListener.java @@ -72,6 +72,7 @@ public class SubscriptionsChangeListener extends ContentObserver { } public void start() { + Log.d(TAG, "Start"); mSubscriptionManager.addOnSubscriptionsChangedListener( mContext.getMainExecutor(), mSubscriptionsChangedListener); mContext.getContentResolver() diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java index f91f78713d..9544d9a281 100644 --- a/src/com/android/settings/network/TetherPreferenceController.java +++ b/src/com/android/settings/network/TetherPreferenceController.java @@ -71,7 +71,11 @@ public class TetherPreferenceController extends AbstractPreferenceController imp } public void onServiceDisconnected(int profile) { - mBluetoothPan.set(null); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothProfile currentProfile = mBluetoothPan.getAndSet(null); + if (currentProfile != null && adapter != null) { + adapter.closeProfileProxy(BluetoothProfile.PAN, currentProfile); + } } }; @@ -127,15 +131,20 @@ public class TetherPreferenceController extends AbstractPreferenceController imp @Override public void onCreate(Bundle savedInstanceState) { - if (mBluetoothAdapter != null && - mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { - mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, - BluetoothProfile.PAN); - } } @Override public void onResume() { + if (mBluetoothAdapter != null && + mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { + final BluetoothProfile profile = mBluetoothPan.get(); + + if (profile == null) { + mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, + BluetoothProfile.PAN); + } + } + if (mAirplaneModeObserver == null) { mAirplaneModeObserver = new SettingObserver(); } @@ -150,6 +159,10 @@ public class TetherPreferenceController extends AbstractPreferenceController imp @Override public void onPause() { + final BluetoothProfile profile = mBluetoothPan.getAndSet(null); + if (profile != null && mBluetoothAdapter != null) { + mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile); + } if (mAirplaneModeObserver != null) { mContext.getContentResolver().unregisterContentObserver(mAirplaneModeObserver); } @@ -160,10 +173,6 @@ public class TetherPreferenceController extends AbstractPreferenceController imp @Override public void onDestroy() { - final BluetoothProfile profile = mBluetoothPan.getAndSet(null); - if (profile != null && mBluetoothAdapter != null) { - mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile); - } } public static boolean isTetherConfigDisallowed(Context context) { diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java index 6113f5a6ae..f05e28911d 100644 --- a/src/com/android/settings/network/UiccSlotUtil.java +++ b/src/com/android/settings/network/UiccSlotUtil.java @@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -69,6 +70,9 @@ public class UiccSlotUtil { if (slotInfos == null) { return ImmutableList.of(); } + slotInfos = Arrays.stream(slotInfos) + .filter(s -> (s != null)) + .toArray(UiccSlotInfo[]::new); return ImmutableList.copyOf(slotInfos); } @@ -92,22 +96,24 @@ public class UiccSlotUtil { Log.i(TAG, "Multiple active slots supported. Not calling switchSlots."); return; } - UiccSlotInfo[] slots = telMgr.getUiccSlotsInfo(); + ImmutableList<UiccSlotInfo> slots = getSlotInfos(telMgr); + int length = slots.size(); if (slotId == INVALID_PHYSICAL_SLOT_ID) { - for (int i = 0; i < slots.length; i++) { - if (slots[i].isRemovable() - && !slots[i].getIsActive() - && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR - && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) { + for (int i = 0; i < length; i++) { + UiccSlotInfo slotInfo = slots.get(i); + if (slotInfo.isRemovable() + && !slotInfo.getIsActive() + && slotInfo.getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR + && slotInfo.getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) { performSwitchToRemovableSlot(i, context); return; } } } else { - if (slotId >= slots.length || !slots[slotId].isRemovable()) { + if (slotId >= length || !slots.get(slotId).isRemovable()) { throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId); } - if (!slots[slotId].getIsActive()) { + if (!slots.get(slotId).getIsActive()) { performSwitchToRemovableSlot(slotId, context); } } diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java index 03db1b89be..5b9714a8d9 100644 --- a/src/com/android/settings/network/apn/ApnEditor.java +++ b/src/com/android/settings/network/apn/ApnEditor.java @@ -55,6 +55,7 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.network.ProxySubscriptionManager; import com.android.settingslib.utils.ThreadUtils; +import com.android.settings.Utils; import java.util.Arrays; import java.util.HashSet; @@ -150,6 +151,8 @@ public class ApnEditor extends SettingsPreferenceFragment private boolean mReadOnlyApn; private Uri mCarrierUri; + private static final String APN_DEFALUT_VALUES_STRING_ARRAY = "apn_default_values_strings_array"; + /** * APN types for data connections. These are usage categories for an APN * entry. One APN entry may support multiple APN types, eg, a single APN @@ -230,6 +233,26 @@ public class ApnEditor extends SettingsPreferenceFragment Telephony.Carriers.USER_EDITABLE //24 }; + private static final String[] sUIConfigurableItems = new String[] { + Telephony.Carriers.NAME, + Telephony.Carriers.APN, + Telephony.Carriers.PROXY, + Telephony.Carriers.PORT, + Telephony.Carriers.USER, + Telephony.Carriers.SERVER, + Telephony.Carriers.PASSWORD, + Telephony.Carriers.MMSC, + Telephony.Carriers.MMSPROXY, + Telephony.Carriers.MMSPORT, + Telephony.Carriers.AUTH_TYPE, + Telephony.Carriers.TYPE, + Telephony.Carriers.PROTOCOL, + Telephony.Carriers.CARRIER_ENABLED, + Telephony.Carriers.BEARER, + Telephony.Carriers.BEARER_BITMASK, + Telephony.Carriers.ROAMING_PROTOCOL, + }; + private static final int ID_INDEX = 0; @VisibleForTesting static final int NAME_INDEX = 1; @@ -311,6 +334,9 @@ public class ApnEditor extends SettingsPreferenceFragment mApnData = getApnDataFromUri(uri); } else { mApnData = new ApnData(sProjection.length); + if (action.equals(Intent.ACTION_INSERT)) { + setDefaultData(); + } } final boolean isUserEdited = mApnData.getInteger(EDITED_INDEX, @@ -598,6 +624,10 @@ public class ApnEditor extends SettingsPreferenceFragment mMvnoType.setValue(mMvnoTypeStr); mMvnoMatchData.setText(mMvnoMatchDataStr); } + String localizedName = Utils.getLocalizedName(getActivity(), mApnData.getString(NAME_INDEX)); + if (!TextUtils.isEmpty(localizedName)) { + mName.setText(localizedName); + } } mName.setSummary(checkNull(mName.getText())); @@ -728,6 +758,11 @@ public class ApnEditor extends SettingsPreferenceFragment telephonyManager = telephonyManagerForSubId; } mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1()); + } else if (values[mvnoIndex].equals("ICCID")) { + if (mMvnoMatchDataStr != null) { + Log.d(TAG, "mMvnoMatchDataStr: " + mMvnoMatchDataStr); + mMvnoMatchData.setText(mMvnoMatchDataStr); + } } else { // mvno type 'none' case. At this time, mvnoIndex should be 0. mMvnoMatchData.setText(""); @@ -1232,6 +1267,51 @@ public class ApnEditor extends SettingsPreferenceFragment return sNotSet.equals(value) ? null : value; } + private void setDefaultData() { + CarrierConfigManager configManager = (CarrierConfigManager) + getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager != null) { + PersistableBundle b = configManager.getConfigForSubId(mSubId); + if (b != null) { + PersistableBundle defaultValues = b.getPersistableBundle( + APN_DEFALUT_VALUES_STRING_ARRAY); + if (defaultValues != null && !defaultValues.isEmpty()) { + Set<String> keys = defaultValues.keySet(); + for (String key : keys) { + if (fieldValidate(key)) { + setAppData(key, defaultValues.get(key)); + } + } + } + } + } + } + + private void setAppData(String key, Object object) { + int index = findIndexOfKey(key); + if (index >= 0) { + mApnData.setObject(index, object); + } + } + + private int findIndexOfKey(String key) { + for(int i = 0; i < sProjection.length; i++) { + if (sProjection[i].equals(key)) { + return i; + } + } + return -1; + } + + private boolean fieldValidate(String field){ + for(String tableField : sUIConfigurableItems){ + if(tableField.equalsIgnoreCase(field)) + return true; + } + Log.w(TAG, field + " is not configurable"); + return false; + } + @VisibleForTesting String getUserEnteredApnType() { // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY" @@ -1467,6 +1547,10 @@ public class ApnEditor extends SettingsPreferenceFragment String getString(int index) { return (String) mData[index]; } + + void setObject(int index, Object value) { + mData[index] = value; + } } private static int getBitmaskForTech(int radioTech) { diff --git a/src/com/android/settings/network/apn/ApnPreference.java b/src/com/android/settings/network/apn/ApnPreference.java index f277db0abd..b9ae6bf3c5 100755 --- a/src/com/android/settings/network/apn/ApnPreference.java +++ b/src/com/android/settings/network/apn/ApnPreference.java @@ -114,6 +114,13 @@ public class ApnPreference extends Preference implements CompoundButton.OnChecke sSelectedKey = getKey(); } + + // ApnPreference.sSelectedKey static variable is shared for MSim case, + // need be initialized according to preferred apn id per sub + public static void setSelectedKey(String preferredApnKey) { + sSelectedKey = preferredApnKey; + } + /** * Change the preference status. */ diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java index 4df2e5ee38..21b752bd16 100755 --- a/src/com/android/settings/network/apn/ApnSettings.java +++ b/src/com/android/settings/network/apn/ApnSettings.java @@ -26,6 +26,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources.NotFoundException; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -40,6 +41,7 @@ import android.provider.Telephony; import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; import android.telephony.PreciseDataConnectionState; +import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -54,13 +56,19 @@ import android.widget.Toast; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; +import com.android.ims.ImsManager; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.network.SubscriptionUtil; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settings.Utils; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; /** Handle each different apn setting. */ public class ApnSettings extends RestrictedSettingsFragment @@ -86,6 +94,8 @@ public class ApnSettings extends RestrictedSettingsFragment Telephony.Carriers.MVNO_TYPE, Telephony.Carriers.MVNO_MATCH_DATA, Telephony.Carriers.EDITED_STATUS, + Telephony.Carriers.BEARER, + Telephony.Carriers.BEARER_BITMASK, }; /** Copied from {@code com.android.internal.telephony.TelephonyIntents} */ @@ -102,6 +112,8 @@ public class ApnSettings extends RestrictedSettingsFragment private static final int MVNO_TYPE_INDEX = 4; private static final int MVNO_MATCH_DATA_INDEX = 5; private static final int EDITED_INDEX = 6; + private static final int BEARER_INDEX = 7; + private static final int BEARER_BITMASK_INDEX = 8; private static final int MENU_NEW = Menu.FIRST; private static final int MENU_RESTORE = Menu.FIRST + 1; @@ -137,6 +149,16 @@ public class ApnSettings extends RestrictedSettingsFragment private boolean mAllowAddingApns; private boolean mHidePresetApnDetails; + private String[] mHideApnsWithRule; + private String[] mHideApnsWithIccidRule; + private PersistableBundle mHideApnsGroupByIccid; + private final static String INCLUDE_COMMON_RULES = "include_common_rules"; + private final static String APN_HIDE_RULE_STRINGS_ARRAY= "apn_hide_rule_strings_array"; + private final static String APN_HIDE_RULE_STRINGS_WITH_ICCIDS_ARRAY = "apn_hide_rule_strings_with_iccids_array"; + + private final static String ACTION_VOLTE_ENABLED_STATE_CHANGED + = "org.codeaurora.intent.action.ACTION_ENHANCE_4G_SWITCH"; + public ApnSettings() { super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); } @@ -183,6 +205,12 @@ public class ApnSettings extends RestrictedSettingsFragment restartPhoneStateListener(mSubId); } fillList(); + } else if (intent.getAction().equals(ACTION_VOLTE_ENABLED_STATE_CHANGED)) { + if (!mRestoreDefaultApnMode) { + fillList(); + } else { + showDialog(DIALOG_RESTORE_DEFAULTAPN); + } } } }; @@ -220,6 +248,9 @@ public class ApnSettings extends RestrictedSettingsFragment mIntentFilter = new IntentFilter(); mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED); mIntentFilter.addAction(ACTION_SIM_STATE_CHANGED); + if (Utils.isSupportCTPA(getActivity().getApplicationContext())) { + mIntentFilter.addAction(ACTION_VOLTE_ENABLED_STATE_CHANGED); + } setIfOnlyAvailableForAdmins(true); @@ -231,6 +262,14 @@ public class ApnSettings extends RestrictedSettingsFragment final PersistableBundle b = configManager.getConfigForSubId(mSubId); mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL); mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); + + mHideApnsWithRule = b.getStringArray(APN_HIDE_RULE_STRINGS_ARRAY); + mHideApnsWithIccidRule = b.getStringArray(APN_HIDE_RULE_STRINGS_WITH_ICCIDS_ARRAY); + if(mSubscriptionInfo != null){ + String iccid = mSubscriptionInfo.getIccId(); + Log.d(TAG, "iccid: " + iccid); + mHideApnsGroupByIccid = b.getPersistableBundle(iccid); + } if (mAllowAddingApns) { final String[] readOnlyApnTypes = b.getStringArray( CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY); @@ -323,10 +362,18 @@ public class ApnSettings extends RestrictedSettingsFragment new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND " + "user_visible!=0"); - if (mHideImsApn) { + int phoneId = SubscriptionManager.getPhoneId(subId); + Context appContext = getActivity().getApplicationContext(); + boolean isVoLTEEnabled = ImsManager.getInstance(appContext, phoneId) + .isEnhanced4gLteModeSettingEnabledByUser(); + if (mHideImsApn || (Utils.isSupportCTPA(appContext) && !isVoLTEEnabled)) { where.append(" AND NOT (type='ims')"); } + appendFilter(where); + + Log.d(TAG, "where = " + where.toString()); + final Cursor cursor = getContentResolver().query(simApnUri, CARRIERS_PROJECTION, where.toString(), null, Telephony.Carriers.DEFAULT_SORT_ORDER); @@ -339,9 +386,13 @@ public class ApnSettings extends RestrictedSettingsFragment final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>(); mSelectedKey = getSelectedApnKey(); + + // ApnPreference.mSelectedKey static variable is shared for MSim case, + // need be initialized according to preferred apn id per sub + ApnPreference.setSelectedKey(mSelectedKey); cursor.moveToFirst(); while (!cursor.isAfterLast()) { - final String name = cursor.getString(NAME_INDEX); + String name = cursor.getString(NAME_INDEX); final String apn = cursor.getString(APN_INDEX); final String key = cursor.getString(ID_INDEX); final String type = cursor.getString(TYPES_INDEX); @@ -349,6 +400,26 @@ public class ApnSettings extends RestrictedSettingsFragment mMvnoType = cursor.getString(MVNO_TYPE_INDEX); mMvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX); + //Special requirement of some operators, need change APN name follow language. + String localizedName = Utils.getLocalizedName(getActivity(), cursor.getString(NAME_INDEX)); + + if (!TextUtils.isEmpty(localizedName)) { + name = localizedName; + } + int bearer = cursor.getInt(BEARER_INDEX); + int bearerBitMask = cursor.getInt(BEARER_BITMASK_INDEX); + int fullBearer = ServiceState.getBitmaskForTech(bearer) | bearerBitMask; + int radioTech = networkTypeToRilRidioTechnology(TelephonyManager.getDefault() + .getDataNetworkType(subId)); + if (!ServiceState.bitmaskHasTech(fullBearer, radioTech) + && (bearer != 0 || bearerBitMask != 0)) { + // In OOS, show APN with bearer as default + if ((radioTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) || (bearer == 0 + && radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) { + cursor.moveToNext(); + continue; + } + } final ApnPreference pref = new ApnPreference(getPrefContext()); pref.setKey(key); @@ -362,8 +433,11 @@ public class ApnSettings extends RestrictedSettingsFragment pref.setSummary(apn); } - final boolean selectable = + boolean selectable = ((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING)); + if (isVoLTEEnabled && selectable && Utils.isSupportCTPA(appContext)) { + selectable = ((type == null) || !type.equals("ims")); + } pref.setSelectable(selectable); if (selectable) { if ((mSelectedKey != null) && mSelectedKey.equals(key)) { @@ -386,6 +460,152 @@ public class ApnSettings extends RestrictedSettingsFragment } } + private void appendFilter(StringBuilder where){ + boolean includeCommon = true; + if(mHideApnsGroupByIccid != null && !mHideApnsGroupByIccid.isEmpty()){ + // APN hidden rules according to the specified iccid, + // it should be configured in CarrierConfig as below. + // <map name="12345"> + // <string name="type">fota</string> + // <boolean name="include_common_rules" value="true"/> + // </map> + includeCommon = mHideApnsGroupByIccid.getBoolean(INCLUDE_COMMON_RULES, true); + Log.d(TAG, "apn hidden rules specified iccid, include common rule: " + includeCommon); + Set<String> keys = mHideApnsGroupByIccid.keySet(); + for(String key : keys){ + if(Utils.carrierTableFieldValidate(key)){ + String value = mHideApnsGroupByIccid.getString(key); + if(value != null){ + where.append(" AND " + key + " <> \"" + value + "\""); + } + } + } + } + + // Some operator have special APN hidden rules group by iccids, + // it should be configured in CarrierConfig as below, + // it maybe overwrite some rules defined in common rules. + // <string-array name="apn_hide_rule_strings_with_iccids_array" num="6"> + // <item value="iccid"/> + // <item value="1111,2222"/> + // <item value="type"/> + // <item value="ims,emergency"/> + // <item value="include_common_rules"/> + // <item value="true"/> + // </string-array> + if(mHideApnsWithIccidRule != null){ + HashMap<String, String> ruleWithIccid = getApnRuleMap(mHideApnsWithIccidRule); + final String iccid = mSubscriptionInfo == null ? "" : mSubscriptionInfo.getIccId(); + if(isOperatorIccid(ruleWithIccid, iccid)){ + String s = ruleWithIccid.get(INCLUDE_COMMON_RULES); + includeCommon = !(s != null && s.equalsIgnoreCase(String.valueOf(false))); + Log.d(TAG, "apn hidden rules in iccids, include common rule: " + includeCommon); + filterWithKey(ruleWithIccid, where); + } + } + + if(includeCommon){ + // Common APN hidden rules, + // it should be configured in CarrierConfig as below. + // <string-array name="apn_default_values_strings_array" num="2"> + // <item value="type"/> + // <item value="fota"/> + // </string-array> + if(mHideApnsWithRule != null){ + HashMap<String, String> rule = getApnRuleMap(mHideApnsWithRule); + filterWithKey(rule, where); + } + } + } + + private void filterWithKey(Map<String, String> rules, StringBuilder where) { + Set<String> fields = rules.keySet(); + for(String field : fields){ + if(Utils.carrierTableFieldValidate(field)){ + String value = rules.get(field); + if(!TextUtils.isEmpty(value)){ + String[] subValues = value.split(","); + for(String subValue : subValues){ + where.append(" AND " + field + " <> \"" + subValue + "\""); + } + } + } + } + } + + private HashMap<String, String> getApnRuleMap(String[] ruleArray) { + HashMap<String, String> rules = new HashMap<String, String>(); + if (ruleArray != null) { + int length = ruleArray.length; + Log.d(TAG, "ruleArray size = " + length); + if (length > 0 && (length % 2 == 0)) { + for (int i = 0; i < length;) { + rules.put(ruleArray[i].toLowerCase(), ruleArray[i + 1]); + i += 2; + } + } + } + return rules; + } + + private boolean isOperatorIccid(HashMap<String, String> ruleMap, String iccid) { + String valuesOfIccid = ruleMap.get("iccid"); + if (!TextUtils.isEmpty(valuesOfIccid)) { + String[] iccids = valuesOfIccid.split(","); + for (String subIccid : iccids) { + if (iccid.startsWith(subIccid.trim())) { + return true; + } + } + } + return false; + } + + private int networkTypeToRilRidioTechnology(int nt) { + switch(nt) { + case TelephonyManager.NETWORK_TYPE_GPRS: + return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS; + case TelephonyManager.NETWORK_TYPE_EDGE: + return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE; + case TelephonyManager.NETWORK_TYPE_UMTS: + return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS; + case TelephonyManager.NETWORK_TYPE_HSDPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA; + case TelephonyManager.NETWORK_TYPE_HSUPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA; + case TelephonyManager.NETWORK_TYPE_HSPA: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA; + case TelephonyManager.NETWORK_TYPE_CDMA: + return ServiceState.RIL_RADIO_TECHNOLOGY_IS95B; + case TelephonyManager.NETWORK_TYPE_1xRTT: + return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT; + case TelephonyManager.NETWORK_TYPE_EVDO_0: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0; + case TelephonyManager.NETWORK_TYPE_EVDO_A: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A; + case TelephonyManager.NETWORK_TYPE_EVDO_B: + return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B; + case TelephonyManager.NETWORK_TYPE_EHRPD: + return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD; + case TelephonyManager.NETWORK_TYPE_LTE: + return ServiceState.RIL_RADIO_TECHNOLOGY_LTE; + case TelephonyManager.NETWORK_TYPE_HSPAP: + return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP; + case TelephonyManager.NETWORK_TYPE_GSM: + return ServiceState.RIL_RADIO_TECHNOLOGY_GSM; + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA; + case TelephonyManager.NETWORK_TYPE_IWLAN: + return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; + case TelephonyManager.NETWORK_TYPE_LTE_CA: + return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA; + case TelephonyManager.NETWORK_TYPE_NR: + return ServiceState.RIL_RADIO_TECHNOLOGY_NR; + default: + return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; + } + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (!mUnavailable) { diff --git a/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java b/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java index 4f64399f52..656019a4b7 100644 --- a/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/BackupCallingPreferenceController.java @@ -18,6 +18,7 @@ package com.android.settings.network.telephony; import android.content.Context; import android.os.PersistableBundle; +import android.os.RemoteException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -33,6 +34,9 @@ import com.android.settings.R; import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.qti.extphone.ExtTelephonyManager; +import com.qti.extphone.ServiceCallback; + import java.util.List; import java.util.Objects; @@ -44,6 +48,9 @@ public class BackupCallingPreferenceController extends TelephonyTogglePreference private static final String LOG_TAG = "BackupCallingPrefCtrl"; private Preference mPreference; + private Context mContext; + private ExtTelephonyManager mExtTelephonyManager; + private boolean mServiceConnected = false; /** * Class constructor of backup calling. @@ -53,8 +60,29 @@ public class BackupCallingPreferenceController extends TelephonyTogglePreference **/ public BackupCallingPreferenceController(Context context, String key) { super(context, key); + mContext = context.getApplicationContext(); + mExtTelephonyManager = ExtTelephonyManager.getInstance(mContext); + mExtTelephonyManager.connectService(mExtTelManagerServiceCallback); } + private ServiceCallback mExtTelManagerServiceCallback = new ServiceCallback() { + + // Since ExtTelephony service is called from TelephonyComponentFactory, + // onConnected() is called even before mExtTelephonyManager.connectService + // as per ExtTelephonyManager#connectService() + @Override + public void onConnected() { + Log.d(LOG_TAG, "mExtTelManagerServiceCallback: service connected"); + mServiceConnected = true; + } + + @Override + public void onDisconnected() { + Log.d(LOG_TAG, "mExtTelManagerServiceCallback: service disconnected"); + mServiceConnected = false; + } + }; + /** * Initialization based on given subscription id. * @@ -76,7 +104,7 @@ public class BackupCallingPreferenceController extends TelephonyTogglePreference if (subInfo == null) { // given subId is not actives return CONDITIONALLY_UNAVAILABLE; } - return (subIdList.size() > 1) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + return (subIdList.size() == 1) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } /** @@ -152,6 +180,22 @@ public class BackupCallingPreferenceController extends TelephonyTogglePreference } protected boolean isCrossSimEnabledByPlatform(Context context, int subscriptionId) { + if (!mServiceConnected) { + Log.d(LOG_TAG, "ExtTelephony service is not connected"); + return false; + } + + try { + if (!mExtTelephonyManager.isEpdgOverCellularDataSupported( + SubscriptionManager.getPhoneId(subscriptionId))) { + Log.d(LOG_TAG, "Not supported by platform. subId = " + subscriptionId); + return false; + } + } catch(RemoteException ex) { + Log.d(LOG_TAG, "isEpdgOverCellularDataSupported Exception" + ex); + return false; + } + // TODO : Change into API which created for accessing // com.android.ims.ImsManager#isCrossSimEnabledByPlatform() if ((new WifiCallingQueryImsState(context, subscriptionId)).isWifiCallingSupported()) { diff --git a/src/com/android/settings/network/telephony/CellInfoUtil.java b/src/com/android/settings/network/telephony/CellInfoUtil.java index 58b668b7d0..0d9dd3f0ae 100644 --- a/src/com/android/settings/network/telephony/CellInfoUtil.java +++ b/src/com/android/settings/network/telephony/CellInfoUtil.java @@ -16,6 +16,7 @@ package com.android.settings.network.telephony; +import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.CellIdentity; import android.telephony.CellIdentityGsm; import android.telephony.CellIdentityLte; @@ -29,6 +30,7 @@ import android.telephony.CellInfoLte; import android.telephony.CellInfoNr; import android.telephony.CellInfoTdscdma; import android.telephony.CellInfoWcdma; +import android.telephony.ServiceState; import android.text.BidiFormatter; import android.text.TextDirectionHeuristics; import android.text.TextUtils; @@ -132,6 +134,139 @@ public final class CellInfoUtil { return ci; } + /** + * Creates a CellInfo object from OperatorInfo for Legacy Incremental Scan results. + */ + public static CellInfo convertLegacyIncrScanOperatorInfoToCellInfo(OperatorInfo operatorInfo) { + final String operatorNumeric = operatorInfo.getOperatorNumeric(); + String mcc = null; + String mnc = null; + String ran = String.valueOf(AccessNetworkType.UNKNOWN); + + if (operatorNumeric != null) { + if (operatorNumeric.matches("^[0-9]{5,6}$")) { + mcc = operatorNumeric.substring(0, 3); + mnc = operatorNumeric.substring(3); + } else if (operatorNumeric.matches("^[0-9]{5,6}[+][0-9]{1,2}$")) { + // If the operator numeric contains the RAN, then parse the MCC-MNC accordingly + String values[] = operatorNumeric.split("\\+"); + mcc = values[0].substring(0, 3); + mnc = values[0].substring(3); + ran = values[1]; + } + } + + CellInfoNr cellInfoNr = null; + CellInfoLte cellInfoLte = null; + CellInfoWcdma cellInfoWcdma = null; + CellInfoGsm cellInfoGsm = null; + CellInfoGsm cellInfoDefault = null; + + // Convert RadioAccessNetwork(ran) to AccessNetworkType + int accessNetworkType = AccessNetworkType.convertRanToAnt(Integer.parseInt(ran)); + + switch(accessNetworkType) { + case AccessNetworkType.NGRAN: + // 5G + CellIdentityNr cellIdentityNr = new CellIdentityNr( + Integer.MAX_VALUE /* pci */, + Integer.MAX_VALUE /* tac */, + Integer.MAX_VALUE /* nrArfcn */, + null /* bands */, + mcc, + mnc, + Integer.MAX_VALUE /* nci */, + operatorInfo.getOperatorAlphaLong() + " 5G", + operatorInfo.getOperatorAlphaShort() + " 5G", + Collections.emptyList()); + cellInfoNr = new CellInfoNr(); + cellInfoNr.setCellIdentity(cellIdentityNr); + break; + + case AccessNetworkType.EUTRAN: + // 4G + CellIdentityLte cellIdentityLte = new CellIdentityLte( + Integer.MAX_VALUE /* ci */, + Integer.MAX_VALUE /* pci */, + Integer.MAX_VALUE /* tac */, + Integer.MAX_VALUE /* earfcn */, + null /* bands */, + Integer.MAX_VALUE /* bandwidth */, + mcc, + mnc, + operatorInfo.getOperatorAlphaLong() + " 4G", + operatorInfo.getOperatorAlphaShort() + " 4G", + Collections.emptyList(), + null /* csgInfo */); + cellInfoLte = new CellInfoLte(); + cellInfoLte.setCellIdentity(cellIdentityLte); + break; + + case AccessNetworkType.UTRAN: + CellIdentityWcdma cellIdentityWcdma = new CellIdentityWcdma( + Integer.MAX_VALUE /* lac */, + Integer.MAX_VALUE /* cid */, + Integer.MAX_VALUE /* psc */, + Integer.MAX_VALUE /* uarfcn */, + mcc, + mnc, + operatorInfo.getOperatorAlphaLong() + " 3G", + operatorInfo.getOperatorAlphaShort() + " 3G", + Collections.emptyList(), + null /* csgInfo */); + cellInfoWcdma = new CellInfoWcdma(); + cellInfoWcdma.setCellIdentity(cellIdentityWcdma); + break; + + case AccessNetworkType.GERAN: + // 2G + CellIdentityGsm cellIdentityGsm = new CellIdentityGsm( + Integer.MAX_VALUE /* lac */, + Integer.MAX_VALUE /* cid */, + Integer.MAX_VALUE /* arfcn */, + Integer.MAX_VALUE /* bsic */, + mcc, + mnc, + operatorInfo.getOperatorAlphaLong() + " 2G", + operatorInfo.getOperatorAlphaShort() + " 2G", + Collections.emptyList()); + cellInfoGsm = new CellInfoGsm(); + cellInfoGsm.setCellIdentity(cellIdentityGsm); + break; + + default: + // This is when RAT info is not present with the PLMN. + // Do not add any network class to the operator name. + CellIdentityGsm cellIdentityDefault = new CellIdentityGsm( + Integer.MAX_VALUE /* lac */, + Integer.MAX_VALUE /* cid */, + Integer.MAX_VALUE /* arfcn */, + Integer.MAX_VALUE /* bsic */, + mcc, + mnc, + operatorInfo.getOperatorAlphaLong(), + operatorInfo.getOperatorAlphaShort(), + Collections.emptyList()); + cellInfoDefault = new CellInfoGsm(); + cellInfoDefault.setCellIdentity(cellIdentityDefault); + break; + } + + CellInfo cellInfo = null; + if (cellInfoNr != null) cellInfo = cellInfoNr; + else if (cellInfoLte != null) cellInfo = cellInfoLte; + else if (cellInfoWcdma != null) cellInfo = cellInfoWcdma; + else if (cellInfoGsm != null) cellInfo = cellInfoGsm; + else cellInfo = cellInfoDefault; + + if (operatorInfo.getState() == OperatorInfo.State.CURRENT) { + // Unlike the legacy full scan, legacy incremental scanning using qcril hooks + // sends the results containing the info about the currently registered operator. + cellInfo.setRegistered(true); + } + return cellInfo; + } + /** Convert a list of cellInfos to readable string without sensitive info. */ public static String cellInfoListToString(List<CellInfo> cellInfos) { return cellInfos.stream() diff --git a/src/com/android/settings/network/telephony/DataDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DataDefaultSubscriptionController.java new file mode 100644 index 0000000000..f4dfbc6769 --- /dev/null +++ b/src/com/android/settings/network/telephony/DataDefaultSubscriptionController.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved + * Not a contribution + * + * 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; + +public class DataDefaultSubscriptionController extends DefaultSubscriptionController { + + private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; + + public DataDefaultSubscriptionController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + protected SubscriptionInfo getDefaultSubscriptionInfo() { + return mManager.getActiveSubscriptionInfo(getDefaultSubscriptionId()); + } + + @Override + protected int getDefaultSubscriptionId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } + + @Override + protected void setDefaultSubscription(int subscriptionId) { + mManager.setDefaultDataSubId(subscriptionId); + setUserPrefDataSubIdInDb(subscriptionId); + } + + private void setUserPrefDataSubIdInDb(int subId) { + android.provider.Settings.Global.putInt(mContext.getContentResolver(), + SETTING_USER_PREF_DATA_SUB, subId); + } +} diff --git a/src/com/android/settings/network/telephony/DataDuringCallsPreferenceController.java b/src/com/android/settings/network/telephony/DataDuringCallsPreferenceController.java index 110c5823de..9f95b9e6a9 100644 --- a/src/com/android/settings/network/telephony/DataDuringCallsPreferenceController.java +++ b/src/com/android/settings/network/telephony/DataDuringCallsPreferenceController.java @@ -69,6 +69,10 @@ public class DataDuringCallsPreferenceController extends TelephonyTogglePreferen mMobileDataContentObserver.setOnMobileDataChangedListener(() -> refreshPreference()); } mMobileDataContentObserver.register(mContext, mSubId); + final int defaultDataSub = SubscriptionManager.getDefaultDataSubscriptionId(); + if (defaultDataSub != mSubId) { + mMobileDataContentObserver.register(mContext, defaultDataSub); + } } @OnLifecycleEvent(ON_PAUSE) @@ -108,6 +112,21 @@ public class DataDuringCallsPreferenceController extends TelephonyTogglePreferen || SubscriptionManager.getDefaultDataSubscriptionId() == subId) { return CONDITIONALLY_UNAVAILABLE; } + boolean isDefDataEnabled = mManager.createForSubscriptionId( + SubscriptionManager.getDefaultDataSubscriptionId()) + .isDataEnabled(); + // Do not show 'Data during calls' preference when mobile data switch + // for the DDS sub is turned off. + if (!isDefDataEnabled) { + return CONDITIONALLY_UNAVAILABLE; + } + + if (TelephonyUtils.isSubsidyFeatureEnabled(mContext) && + !TelephonyUtils.isSubsidySimCard(mContext, + SubscriptionManager.getSlotIndex(mSubId))) { + return CONDITIONALLY_UNAVAILABLE; + } + return AVAILABLE; } diff --git a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java index 168e69df2b..1a20f2bb9f 100644 --- a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java +++ b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java @@ -21,12 +21,18 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import android.content.ComponentName; import android.content.Context; +import android.provider.Settings; +import android.sysprop.TelephonyProperties; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.PhoneStateListener; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.view.View; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; @@ -44,6 +50,8 @@ import com.android.settings.network.SubscriptionsChangeListener; import java.util.ArrayList; import java.util.List; +import org.codeaurora.internal.IExtTelephony; + /** * This implements common controller functionality for a Preference letting the user see/change * what mobile network subscription is used by default for some service controlled by the @@ -65,12 +73,28 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere "com.android.services.telephony.TelephonyConnectionService"); private boolean mIsRtlMode; + protected TelephonyManager mTelephonyManager; + + //String keys for data preference lookup + private static final String LIST_DATA_PREFERENCE_KEY = "data_preference"; + + private int mPhoneCount; + private PhoneStateListener[] mPhoneStateListener; + private int[] mCallState; + private ArrayList<SubscriptionInfo> mSelectableSubs; + public DefaultSubscriptionController(Context context, String preferenceKey) { super(context, preferenceKey); mManager = context.getSystemService(SubscriptionManager.class); mChangeListener = new SubscriptionsChangeListener(context, this); mIsRtlMode = context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + mTelephonyManager = (TelephonyManager) mContext + .getSystemService(Context.TELEPHONY_SERVICE); + mPhoneCount = mTelephonyManager.getPhoneCount(); + mPhoneStateListener = new PhoneStateListener[mPhoneCount]; + mCallState = new int[mPhoneCount]; + mSelectableSubs = new ArrayList<SubscriptionInfo>(); } public void init(Lifecycle lifecycle) { @@ -94,18 +118,27 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere @Override public int getAvailabilityStatus(int subId) { - return AVAILABLE; + boolean visible = false; + if (mSelectableSubs != null && mSelectableSubs.size() > 1) { + visible = true; + } else { + visible = false; + } + + return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @OnLifecycleEvent(ON_RESUME) public void onResume() { mChangeListener.start(); + registerPhoneStateListener(); updateEntries(); } @OnLifecycleEvent(ON_PAUSE) public void onPause() { mChangeListener.stop(); + unRegisterPhoneStateListener(); } @Override @@ -139,6 +172,13 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere if (mPreference == null) { return; } + + updateSubStatus(); + if (mSelectableSubs.isEmpty()) { + Log.d(TAG, "updateEntries: mSelectable subs is empty"); + return; + } + if (!isAvailable()) { mPreference.setVisible(false); return; @@ -150,24 +190,22 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere // getAvailabilityStatus returned CONDITIONALLY_UNAVAILABLE at the time. mPreference.setOnPreferenceChangeListener(this); - final List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(mManager); - // We'll have one entry for each available subscription, plus one for a "ask me every // time" entry at the end. final ArrayList<CharSequence> displayNames = new ArrayList<>(); final ArrayList<CharSequence> subscriptionIds = new ArrayList<>(); - if (subs.size() == 1) { + if (mSelectableSubs.size() == 1) { mPreference.setEnabled(false); mPreference.setSummary(SubscriptionUtil.getUniqueSubscriptionDisplayName( - subs.get(0), mContext)); + mSelectableSubs.get(0), mContext)); return; } final int serviceDefaultSubId = getDefaultSubscriptionId(); boolean subIsAvailable = false; - for (SubscriptionInfo sub : subs) { + for (SubscriptionInfo sub : mSelectableSubs) { if (sub.isOpportunistic()) { continue; } @@ -178,14 +216,30 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere subIsAvailable = true; } } + if (TextUtils.equals(getPreferenceKey(), LIST_DATA_PREFERENCE_KEY)) { + boolean isEcbmEnabled = mTelephonyManager.getEmergencyCallbackMode(); + boolean isScbmEnabled = TelephonyProperties.in_scbm().orElse(false); + + int isSmartDdsEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SMART_DDS_SWITCH, 0); + + if (isSmartDdsEnabled == 0) { + mPreference.setEnabled(isCallStateIdle() && !isEcbmEnabled && !isScbmEnabled && + (!TelephonyUtils.isSubsidyFeatureEnabled(mContext) || + TelephonyUtils.allowUsertoSetDDS(mContext))); + } else { + mPreference.setEnabled(false); + mPreference.setSummary("Smart DDS switch is on"); + } - if (isAskEverytimeSupported()) { - // Add the extra "Ask every time" value at the end. - displayNames.add(mContext.getString(R.string.calls_and_sms_ask_every_time)); - subscriptionIds.add(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); + } else { + if (isAskEverytimeSupported()) { + // Add the extra "Ask every time" value at the end. + displayNames.add(mContext.getString(R.string.calls_and_sms_ask_every_time)); + subscriptionIds.add(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); + } } - mPreference.setEnabled(true); mPreference.setEntries(displayNames.toArray(new CharSequence[0])); mPreference.setEntryValues(subscriptionIds.toArray(new CharSequence[0])); @@ -266,6 +320,16 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere return (label != null) ? label : ""; } + private boolean isCallStateIdle() { + boolean callStateIdle = true; + for (int i = 0; i < mPhoneCount; i++) { + if (TelephonyManager.CALL_STATE_IDLE != mCallState[i]) { + callStateIdle = false; + } + } + return callStateIdle; + } + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final int subscriptionId = Integer.parseInt((String) newValue); @@ -280,6 +344,9 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere @Override public void onSubscriptionsChanged() { + if (mSelectableSubs != null) mSelectableSubs.clear(); + updateSubStatus(); + if (mPreference != null) { updateEntries(); refreshSummary(mPreference); @@ -289,4 +356,53 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere boolean isRtlMode() { return mIsRtlMode; } + + private void registerPhoneStateListener() { + //To make sure subinfo is added, before registering for call state change + updateSubStatus(); + + for (int i = 0; i < mSelectableSubs.size(); i++) { + int subId = mSelectableSubs.get(i).getSubscriptionId(); + TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId); + tm.listen(getPhoneStateListener(i), + PhoneStateListener.LISTEN_CALL_STATE); + } + } + + private void unRegisterPhoneStateListener() { + for (int i = 0; i < mPhoneCount; i++) { + if (mPhoneStateListener[i] != null) { + mTelephonyManager.listen(mPhoneStateListener[i], PhoneStateListener.LISTEN_NONE); + mPhoneStateListener[i] = null; + } + } + } + + private PhoneStateListener getPhoneStateListener(int phoneId) { + // Disable Sim selection for Data when voice call is going on as changing the default data + // sim causes a modem reset currently and call gets disconnected + final int i = phoneId; + mPhoneStateListener[phoneId] = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + mCallState[i] = state; + updateEntries(); + } + }; + return mPhoneStateListener[phoneId]; + } + + private void updateSubStatus() { + if (!mSelectableSubs.isEmpty()) { + return; + } + + for (int i = 0; i < mPhoneCount; ++i) { + final SubscriptionInfo sir = mManager + .getActiveSubscriptionInfoForSimSlotIndex(i); + if (sir != null) { + mSelectableSubs.add(sir); + } + } + } } diff --git a/src/com/android/settings/network/telephony/Enabled5GPreferenceController.java b/src/com/android/settings/network/telephony/Enabled5GPreferenceController.java new file mode 100755 index 0000000000..fbd78d7a56 --- /dev/null +++ b/src/com/android/settings/network/telephony/Enabled5GPreferenceController.java @@ -0,0 +1,341 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +package com.android.settings.network.telephony; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.PersistableBundle; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import com.android.internal.telephony.TelephonyIntents; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import com.android.settings.R; +import com.android.settings.network.AllowedNetworkTypesListener; +import com.android.settings.network.telephony.MobileNetworkUtils; + + +/** + * Preference controller for "Enabled 5G Switch" +*/ +public class Enabled5GPreferenceController extends TelephonyTogglePreferenceController + implements LifecycleObserver, OnStart, OnStop { + private static final String TAG = "Enable5g"; + private static final int NETWORK_MODE_TYPE_INVALID = -1; + private static final String USER_SELECTED_NW_MODE_KEY = "user_selected_network_type_"; + + Preference mPreference; + private PhoneCallStateListener mPhoneStateListener; + private TelephonyManager mTelephonyManager; + private AllowedNetworkTypesListener mAllowedNetworkTypesListener; + @VisibleForTesting + Integer mCallState; + + private ContentObserver mSubsidySettingsObserver; + /* + * Indicates whether this SUB has NR capability or not. + */ + private boolean mIsNrRadioSupported = false; + /* + * Indicates whether NR can be registered on both SUBs at the same time. + */ + private boolean mIsDualNrSupported = false; + + private SharedPreferences mSharedPreferences; + private boolean mChangedBy5gToggle = false; + + private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mPreference != null) { + Log.d(TAG,"DDS is changed"); + updateState(mPreference); + } + } + }; + public Enabled5GPreferenceController(Context context, String key) { + super(context, key); + } + + public Enabled5GPreferenceController init(int subId) { + if (mPhoneStateListener == null) { + mPhoneStateListener = new PhoneCallStateListener(); + } + + if (SubscriptionManager.isValidSubscriptionId(mSubId) && mSubId == subId) { + return this; + } + mSubId = subId; + mTelephonyManager = mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(mSubId); + mIsNrRadioSupported = + checkSupportedRadioBitmask(mTelephonyManager.getSupportedRadioAccessFamily(), + TelephonyManager.NETWORK_TYPE_BITMASK_NR); + mIsDualNrSupported = TelephonyUtils.isDual5gSupported(mTelephonyManager); + if (mAllowedNetworkTypesListener == null) { + mAllowedNetworkTypesListener = new AllowedNetworkTypesListener( + mContext.getMainExecutor()); + mAllowedNetworkTypesListener.setAllowedNetworkTypesListener( + () -> update()); + } + mSharedPreferences = mContext.getSharedPreferences(mContext.getPackageName(), + mContext.MODE_PRIVATE); + return this; + } + + private void update() { + Log.d(TAG, "update."); + updatePreference(); + //if user select network mode from prefered network list, then reset cache to invalid. + if (!mChangedBy5gToggle) { + cachePreviousSelectedNwType(NETWORK_MODE_TYPE_INVALID); + } + mChangedBy5gToggle = false; + } + private void updatePreference() { + if (mPreference != null) { + updateState(mPreference); + } + } + + @Override + public int getAvailabilityStatus(int subId) { + final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId); + if (carrierConfig == null || mTelephonyManager == null) { + return CONDITIONALLY_UNAVAILABLE; + } + int defaultDdsSubId = SubscriptionManager.getDefaultDataSubscriptionId(); + final boolean isSingleNrSupportedOnly = + !mIsDualNrSupported && (defaultDdsSubId == subId); + final boolean isNrAllowed = + checkSupportedRadioBitmask(mTelephonyManager.getAllowedNetworkTypes(), + TelephonyManager.NETWORK_TYPE_BITMASK_NR); + final boolean isVisible = SubscriptionManager.isValidSubscriptionId(subId) + && !carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENABLED_5G_BOOL) + && mIsNrRadioSupported + && isNrAllowed + && (mIsDualNrSupported || isSingleNrSupportedOnly); + return isVisible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public void onStart() { + mContext.registerReceiver(mDefaultDataChangedReceiver, + new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)); + if (mPhoneStateListener != null) { + mPhoneStateListener.register(mContext, mSubId); + } + if (mAllowedNetworkTypesListener != null) { + mAllowedNetworkTypesListener.register(mContext, mSubId); + } + } + + @Override + public void onStop() { + if (mDefaultDataChangedReceiver != null) { + mContext.unregisterReceiver(mDefaultDataChangedReceiver); + } + if (mPhoneStateListener != null) { + mPhoneStateListener.unregister(); + } + if (mAllowedNetworkTypesListener != null) { + mAllowedNetworkTypesListener.unregister(mContext, mSubId); + } + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + final SwitchPreference switchPreference = (SwitchPreference) preference; + switchPreference.setVisible(isAvailable()); + long preferredNetworkBitMask = MobileNetworkUtils.getRafFromNetworkType( + getAllowedNetworkMode()); + switchPreference.setChecked(isNrNetworkModeType(preferredNetworkBitMask)); + switchPreference.setEnabled(isCallStateIdle()); + } + + @Override + public boolean setChecked(boolean isChecked) { + if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { + return false; + } + int oldNetworkMode = getAllowedNetworkMode(); + long newNetworkBitMask; + if (TelephonyManager.NETWORK_MODE_NR_ONLY != oldNetworkMode) { + long oldNetworkBitMask = MobileNetworkUtils.getRafFromNetworkType(oldNetworkMode); + if (isChecked) { + long networkTypeBitmap4g = oldNetworkBitMask + & TelephonyManager.NETWORK_CLASS_BITMASK_4G; + long networkTypeBitmap3g = oldNetworkBitMask + & TelephonyManager.NETWORK_CLASS_BITMASK_3G; + if (networkTypeBitmap4g == 0 && networkTypeBitmap3g == 0) { + //Enable from 2G to 5G. + //Use NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA as default value + //with LTE + oldNetworkBitMask = MobileNetworkUtils.getRafFromNetworkType( + TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA); + cachePreviousSelectedNwType(oldNetworkMode); + } else if(networkTypeBitmap4g == 0) { + //Enable from 3G to 5G. + //For EVDO only, map to TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO + //as no proper mapping value include LTE. + if (oldNetworkMode == TelephonyManager.NETWORK_MODE_EVDO_NO_CDMA) { + oldNetworkBitMask = MobileNetworkUtils.getRafFromNetworkType( + TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO); + } else { + oldNetworkBitMask = oldNetworkBitMask + | TelephonyManager.NETWORK_TYPE_BITMASK_LTE; + } + cachePreviousSelectedNwType(oldNetworkMode); + } else { + cachePreviousSelectedNwType(NETWORK_MODE_TYPE_INVALID); + } + } + int userSelectedNwMode = getPreviousSelectedNwType(); + if ((userSelectedNwMode != NETWORK_MODE_TYPE_INVALID) && !isChecked) { + Log.d(TAG, "userSelectedNwMode: " + userSelectedNwMode); + newNetworkBitMask = MobileNetworkUtils + .getRafFromNetworkType(userSelectedNwMode); + cachePreviousSelectedNwType(NETWORK_MODE_TYPE_INVALID); + } else { + newNetworkBitMask = isChecked ? + (oldNetworkBitMask | TelephonyManager.NETWORK_TYPE_BITMASK_NR) + : (oldNetworkBitMask & ~TelephonyManager.NETWORK_TYPE_BITMASK_NR); + } + } else { + newNetworkBitMask = MobileNetworkUtils + .getRafFromNetworkType(TelephonyManager.NETWORK_MODE_LTE_ONLY); + } + mTelephonyManager.setAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, newNetworkBitMask); + mChangedBy5gToggle = true; + return true; + } + + private void cachePreviousSelectedNwType(int oldNetworkMode) { + Log.d(TAG, "cachePreviousSelectedNwType: " + oldNetworkMode); + int slotId = SubscriptionManager.getSlotIndex(mSubId); + mSharedPreferences.edit() + .putInt(USER_SELECTED_NW_MODE_KEY + slotId, oldNetworkMode).apply(); + } + + private int getPreviousSelectedNwType() { + int slotId = SubscriptionManager.getSlotIndex(mSubId); + return mSharedPreferences.getInt(USER_SELECTED_NW_MODE_KEY + + slotId, NETWORK_MODE_TYPE_INVALID); + } + + private int getAllowedNetworkMode() { + return MobileNetworkUtils.getNetworkTypeFromRaf( + (int) mTelephonyManager.getAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); + } + + @Override + public boolean isChecked(){ + long preNetworkBitMask = MobileNetworkUtils.getRafFromNetworkType( + getAllowedNetworkMode()); + return isNrNetworkModeType(preNetworkBitMask); + } + + private boolean isNrNetworkModeType(long currentRadioBitmask) { + return checkSupportedRadioBitmask(currentRadioBitmask, + TelephonyManager.NETWORK_TYPE_BITMASK_NR); + } + + boolean checkSupportedRadioBitmask(long supportedRadioBitmask, long targetBitmask) { + Log.d(TAG, "supportedRadioBitmask: " + supportedRadioBitmask); + if ((targetBitmask & supportedRadioBitmask) > 0) { + return true; + } + return false; + } + + boolean isCallStateIdle() { + boolean callStateIdle = true; + if (mCallState != null && mCallState != TelephonyManager.CALL_STATE_IDLE) { + callStateIdle = false; + } + Log.d(TAG, "isCallStateIdle:" + callStateIdle); + return callStateIdle; + } + + private class PhoneCallStateListener extends PhoneStateListener { + + PhoneCallStateListener() { + super(Looper.getMainLooper()); + } + + private TelephonyManager mTelephonyManager; + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + mCallState = state; + updateState(mPreference); + } + + public void register(Context context, int subId) { + mTelephonyManager = context.getSystemService(TelephonyManager.class); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); + } + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); + + } + + public void unregister() { + mCallState = null; + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); + } + } +} diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java index 25e0ae0765..8f4894054c 100644 --- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java @@ -20,12 +20,18 @@ import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; import android.os.PersistableBundle; +import android.provider.Settings; import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; @@ -59,6 +65,9 @@ public class EnabledNetworkModePreferenceController extends private CarrierConfigManager mCarrierConfigManager; private PreferenceEntriesBuilder mBuilder; private SubscriptionsChangeListener mSubscriptionsListener; + private PhoneCallStateListener mPhoneStateListener; + @VisibleForTesting + Integer mCallState; public EnabledNetworkModePreferenceController(Context context, String key) { super(context, key); @@ -94,6 +103,10 @@ public class EnabledNetworkModePreferenceController extends if (mAllowedNetworkTypesListener == null) { return; } + if (mPhoneStateListener != null) { + mPhoneStateListener.register(mContext, mSubId); + } + mAllowedNetworkTypesListener.register(mContext, mSubId); } @@ -103,6 +116,9 @@ public class EnabledNetworkModePreferenceController extends if (mAllowedNetworkTypesListener == null) { return; } + if (mPhoneStateListener != null) { + mPhoneStateListener.unregister(); + } mAllowedNetworkTypesListener.unregister(mContext, mSubId); } @@ -125,6 +141,7 @@ public class EnabledNetworkModePreferenceController extends listPreference.setEntryValues(mBuilder.getEntryValues()); listPreference.setValue(Integer.toString(mBuilder.getSelectedEntryValue())); listPreference.setSummary(mBuilder.getSummary()); + listPreference.setEnabled(isCallStateIdle()); } @Override @@ -148,6 +165,9 @@ public class EnabledNetworkModePreferenceController extends .createForSubscriptionId(mSubId); mBuilder = new PreferenceEntriesBuilder(mContext, mSubId); + if (mPhoneStateListener == null) { + mPhoneStateListener = new PhoneCallStateListener(); + } if (mAllowedNetworkTypesListener == null) { mAllowedNetworkTypesListener = new AllowedNetworkTypesListener( mContext.getMainExecutor()); @@ -353,7 +373,7 @@ public class EnabledNetworkModePreferenceController extends if (entryValuesInt.length < 3) { throw new IllegalArgumentException("ENABLED_NETWORKS_CHOICES index error."); } - add5gEntry(addNrToLteNetworkType(entryValuesInt[0])); + add5gLteEntry(addNrToLteNetworkType(entryValuesInt[0])); addLteEntry(entryValuesInt[0]); add3gEntry(entryValuesInt[1]); add2gEntry(entryValuesInt[2]); @@ -589,7 +609,9 @@ public class EnabledNetworkModePreferenceController extends case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_WCDMA: setSelectedEntry( TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA); - setSummary(getResourcesForSubId().getString(R.string.network_5G_recommended)); + setSummary((mShow4gForLTE ? getResourcesForSubId().getString(R.string.network_5G_recommended) + : getResourcesForSubId().getString(R.string.network_5G_lte)) + + getResourcesForSubId().getString(R.string.network_5G_recommended)); break; case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA: case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM: @@ -598,11 +620,15 @@ public class EnabledNetworkModePreferenceController extends case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: setSelectedEntry(TelephonyManagerConstants .NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA); - setSummary(getResourcesForSubId().getString(R.string.network_5G_recommended)); + setSummary((mShow4gForLTE ? getResourcesForSubId().getString(R.string.network_5G_recommended) + : getResourcesForSubId().getString(R.string.network_5G_lte)) + + getResourcesForSubId().getString(R.string.network_5G_recommended)); break; case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO: setSelectedEntry(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO); - setSummary(getResourcesForSubId().getString(R.string.network_5G_recommended)); + setSummary((mShow4gForLTE ? getResourcesForSubId().getString(R.string.network_5G_recommended) + : getResourcesForSubId().getString(R.string.network_5G_lte)) + + getResourcesForSubId().getString(R.string.network_5G_recommended)); break; case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA: setSelectedEntry( @@ -612,8 +638,9 @@ public class EnabledNetworkModePreferenceController extends || MobileNetworkUtils.isWorldMode(mContext, mSubId)) { setSummary(R.string.network_global); } else { - setSummary(getResourcesForSubId().getString( - R.string.network_5G_recommended)); + setSummary((mShow4gForLTE ? getResourcesForSubId().getString(R.string.network_5G_recommended) + : getResourcesForSubId().getString(R.string.network_5G_lte)) + + getResourcesForSubId().getString(R.string.network_5G_recommended)); } break; default: @@ -717,6 +744,23 @@ public class EnabledNetworkModePreferenceController extends } } + // This entry is used to support for 5G/LTE display in resource overlay + private void add5gLteEntry(int value) { + boolean isNRValue = value >= TelephonyManagerConstants.NETWORK_MODE_NR_ONLY; + if (showNrList() && isNRValue) { + mEntries.add(mContext.getString(R.string.network_5G_lte) + + mContext.getString(R.string.network_5G_recommended)); + mEntriesValue.add(value); + mIs5gEntryDisplayed = true; + } else { + mIs5gEntryDisplayed = false; + Log.d(LOG_TAG, "Hide 5G option. " + + " supported5GRadioAccessFamily: " + mSupported5gRadioAccessFamily + + " allowed5GNetworkType: " + mAllowed5gNetworkType + + " isNRValue: " + isNRValue); + } + } + private void addGlobalEntry(int value) { Log.d(LOG_TAG, "addGlobalEntry. " + " supported5GRadioAccessFamily: " + mSupported5gRadioAccessFamily @@ -836,4 +880,42 @@ public class EnabledNetworkModePreferenceController extends public void onSubscriptionsChanged() { mBuilder.updateConfig(); } + + private boolean isCallStateIdle() { + boolean callStateIdle = true; + if (mCallState != null && mCallState != TelephonyManager.CALL_STATE_IDLE) { + callStateIdle = false; + } + Log.d(LOG_TAG, "isCallStateIdle:" + callStateIdle); + return callStateIdle; + } + + private class PhoneCallStateListener extends PhoneStateListener { + + PhoneCallStateListener() { + super(Looper.getMainLooper()); + } + + private TelephonyManager mTelephonyManager; + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + mCallState = state; + updateState(mPreference); + } + + public void register(Context context, int subId) { + mTelephonyManager = context.getSystemService(TelephonyManager.class); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); + } + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); + + } + + public void unregister() { + mCallState = null; + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); + } + } } diff --git a/src/com/android/settings/network/telephony/LegacyIncrementalScanBroadcastReceiver.java b/src/com/android/settings/network/telephony/LegacyIncrementalScanBroadcastReceiver.java new file mode 100644 index 0000000000..a72f691d93 --- /dev/null +++ b/src/com/android/settings/network/telephony/LegacyIncrementalScanBroadcastReceiver.java @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.settings.network.telephony; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.telephony.CellInfo; +import android.telephony.CellInfoGsm; +import android.telephony.NetworkScan; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.TelephonyScanManager; +import android.util.Log; + +import com.android.internal.telephony.OperatorInfo; +import com.android.settings.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class receives the incremental scan results intent from QCRIL Message Tunnel, processes it + * and sends them to the network search results activity {@link NetworkSelectSettings.java}. + */ + +public class LegacyIncrementalScanBroadcastReceiver extends BroadcastReceiver { + private static final String TAG = "LegacyIncrementalScanBroadcastReceiver"; + private static final String ACTION_INCREMENTAL_NW_SCAN_IND + = "qualcomm.intent.action.ACTION_INCREMENTAL_NW_SCAN_IND"; + + private static final String EXTRA_SCAN_RESULT = "scan_result"; + private static final String EXTRA_INCREMENTAL_SCAN_DATA = "incr_nw_scan_data"; + private static final String EXTRA_INSTANCE_ID = "sub_id"; + + private static final int QUERY_EXCEPTION = -1; + + // Network scan was successful and complete + private static final int NAS_QUERY_COMPLETE = 0; + // Network scan was partial + private static final int NAS_QUERY_PARTIAL = 1; + // Network scan was aborted + private static final int NAS_QUERY_ABORT = 2; + // Network scan did not complete due to a radio link failure recovery in progress + private static final int NAS_QUERY_REJ_IN_RLF = 3; + // Sending incremental network scan errors + private static final int NAS_QUERY_INCREMENT_ERROR = 4; + // Periodic network scan gave partial results + private static final int NAS_QUERY_PARTIAL_PERIODIC = 5; + + private static int sPhoneCount; + private Context mContext; + + // QueryDetails for each phoneId + private QueryDetails[] mQueryDetails; + + private final TelephonyScanManager.NetworkScanCallback mNetworkScanCallback; + + // TODO: This class may not be required since handling of incremental results is already + // being taken care of in {@link NetworkSelectSettings} class. + class QueryDetails { + String[] storedScanInfo; + + QueryDetails() { + storedScanInfo = null; + } + + void concatScanInfo(String[] scanInfo) { + String[] concatScanInfo = new String[storedScanInfo.length + scanInfo.length]; + System.arraycopy(storedScanInfo, 0, concatScanInfo, 0, storedScanInfo.length); + System.arraycopy(scanInfo, 0, concatScanInfo, storedScanInfo.length, + scanInfo.length); + storedScanInfo = concatScanInfo; + } + + void reset() { + storedScanInfo = null; + } + } + + public LegacyIncrementalScanBroadcastReceiver(Context context, + TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback) { + mContext = context; + TelephonyManager tm = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + sPhoneCount = tm.getActiveModemCount(); + mQueryDetails = new QueryDetails[sPhoneCount]; + for (int i = 0; i < sPhoneCount; i++) { + mQueryDetails[i] = new QueryDetails(); + } + mNetworkScanCallback = mInternalNetworkScanCallback; + } + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "onReceive " + intent); + if (ACTION_INCREMENTAL_NW_SCAN_IND.equals(intent.getAction())) { + broadcastIncrementalQueryResults(intent); + } + } + + private void onResults(List<CellInfo> results) { + mNetworkScanCallback.onResults(results); + } + + private void onComplete() { + mNetworkScanCallback.onComplete(); + } + + private void onError(int error) { + mNetworkScanCallback.onError(error); + } + + private void broadcastIncrementalQueryResults(Intent intent) { + int result = intent.getIntExtra(EXTRA_SCAN_RESULT, QUERY_EXCEPTION); + int phoneId = intent.getIntExtra(EXTRA_INSTANCE_ID, + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + + Log.d(TAG, "broadcastIncrementalQueryResults: phoneid: " + phoneId + ", result: " + result); + + if (phoneId < 0 || phoneId >= sPhoneCount) { + // Invalid phoneId + onError(NetworkScan.ERROR_INVALID_SCAN); + return; + } + + if (result == NAS_QUERY_REJ_IN_RLF) { + onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR); + return; + } + + if (result == NAS_QUERY_ABORT) { + onError(NetworkScan.ERROR_MODEM_ERROR); + return; + } + + if (result == NAS_QUERY_COMPLETE || result == NAS_QUERY_PARTIAL) { + String[] scanInfo = intent.getStringArrayExtra(EXTRA_INCREMENTAL_SCAN_DATA); + QueryDetails queryDetails = mQueryDetails[phoneId]; + + Log.d(TAG, "broadcastIncrementalQueryResults" + + ", scanInfo.length: " + (scanInfo == null ? 0 : scanInfo.length)); + + if (queryDetails.storedScanInfo != null && scanInfo != null) { + queryDetails.concatScanInfo(scanInfo); + } else { + queryDetails.storedScanInfo = scanInfo; + } + + if (queryDetails.storedScanInfo != null) { + List<CellInfo> cellInfos = getCellInfosFromScanResult(queryDetails.storedScanInfo); + onResults(cellInfos); + } + + if (result == NAS_QUERY_COMPLETE) { + // Clear the cache, otherwise the results for the next scan will be combined with + // the current one. + queryDetails.reset(); + onComplete(); + } + } + } + + private List<CellInfo> getCellInfosFromScanResult(String[] scanInfos) { + Log.d(TAG, "Number of operators: " + (scanInfos.length)/4); + List<CellInfo> cellInfoList = new ArrayList<CellInfo>(); + if (scanInfos.length >= 4 && (scanInfos.length % 4 == 0)) { + // The scan results are grouped into four elements per operator. + for (int i = 0; i < scanInfos.length / 4; i++) { + int j = 4 * i; + String operatorAlphaLong = scanInfos[0 + j]; + String operatorAlphaShort = scanInfos[1 + j]; + String operatorNumeric = scanInfos[2 + j]; + String operatorStateString = scanInfos[3 + j]; + + OperatorInfo operatorInfo = new OperatorInfo(operatorAlphaLong, + operatorAlphaShort, + operatorNumeric, + operatorStateString); + + CellInfo cellinfo = + CellInfoUtil.convertLegacyIncrScanOperatorInfoToCellInfo(operatorInfo); + + Log.d(TAG, "OperatorInfo: " + operatorInfo.toString() + + " CellInfo: " + CellInfoUtil.cellInfoToString(cellinfo)); + + cellInfoList.add(cellinfo); + } + } + return cellInfoList; + } +} diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java index 1ac121e56f..aaba307603 100644 --- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java +++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -32,11 +33,16 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.network.MobileDataContentObserver; +import com.android.settings.network.SubscriptionUtil; import com.android.settings.wifi.WifiPickerTrackerHelper; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + /** * Preference controller for "Mobile data" */ @@ -56,6 +62,7 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon boolean mNeedDialog; private WifiPickerTrackerHelper mWifiPickerTrackerHelper; + private AnotherSubCallStateListener mCallStateListener; public MobileDataPreferenceController(Context context, String key) { super(context, key); @@ -81,6 +88,10 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon public void onStart() { if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { mDataContentObserver.register(mContext, mSubId); + // Listen if voice call is on nDDS SUB. + if (mSubId == mSubscriptionManager.getDefaultDataSubscriptionId()) { + mCallStateListener.register(mContext, mSubId); + } } } @@ -88,6 +99,7 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon public void onStop() { if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { mDataContentObserver.unRegister(mContext); + mCallStateListener.unregister(); } } @@ -132,8 +144,20 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon preference.setEnabled(false); preference.setSummary(R.string.mobile_data_settings_summary_auto_switch); } else { - preference.setEnabled(true); - preference.setSummary(R.string.mobile_data_settings_summary); + if (!mCallStateListener.isIdle()) { + preference.setEnabled(false); + preference.setSummary( + R.string.mobile_data_settings_summary_default_data_unavailable); + } else { + if (TelephonyUtils.isSubsidyFeatureEnabled(mContext) && + !TelephonyUtils.isSubsidySimCard(mContext, + mSubscriptionManager.getSlotIndex(mSubId))) { + preference.setEnabled(false); + } else { + preference.setEnabled(true); + } + preference.setSummary(R.string.mobile_data_settings_summary); + } } if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { @@ -154,6 +178,10 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon mSubId = subId; mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); + mCallStateListener = + new AnotherSubCallStateListener(mTelephonyManager, + mSubscriptionManager, + ()-> updateState(mPreference)); } public void setWifiPickerTrackerHelper(WifiPickerTrackerHelper helper) { @@ -165,8 +193,14 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon final boolean enableData = !isChecked(); final boolean isMultiSim = (mTelephonyManager.getActiveModemCount() > 1); final int defaultSubId = mSubscriptionManager.getDefaultDataSubscriptionId(); - final boolean needToDisableOthers = mSubscriptionManager + boolean needToDisableOthers = mSubscriptionManager .isActiveSubscriptionId(defaultSubId) && defaultSubId != mSubId; + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_voice_data_sms_auto_fallback)) { + // Mobile data of both subscriptions can be enabled + // simultaneously. DDS setting will be controlled by the config. + needToDisableOthers = false; + } if (enableData && isMultiSim && needToDisableOthers) { mDialogType = MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG; return true; @@ -179,4 +213,53 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon mSubId); dialogFragment.show(mFragmentManager, DIALOG_TAG); } + + private static class AnotherSubCallStateListener extends TelephonyCallback + implements TelephonyCallback.CallStateListener { + private Runnable mRunnable; + private int mState = TelephonyManager.CALL_STATE_IDLE; + private Map<Integer, AnotherSubCallStateListener> mCallbacks; + private TelephonyManager mTelephonyManager; + private SubscriptionManager mSubscriptionManager; + + public AnotherSubCallStateListener(TelephonyManager tm, SubscriptionManager sm, + Runnable runnable) { + mTelephonyManager = tm; + mSubscriptionManager = sm; + mRunnable = runnable; + mCallbacks = new TreeMap<>(); + } + + public void register(Context context, int subId) { + final List<SubscriptionInfo> subs = + SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager); + for (SubscriptionInfo subInfo : subs) { + if (subInfo.getSubscriptionId() != subId) { + mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()) + .registerTelephonyCallback(context.getMainExecutor(), this); + mCallbacks.put(subInfo.getSubscriptionId(), this); + } + } + } + + public void unregister() { + for (int subId : mCallbacks.keySet()) { + mTelephonyManager.createForSubscriptionId(subId) + .unregisterTelephonyCallback(mCallbacks.get(subId)); + } + mCallbacks.clear(); + } + + public boolean isIdle() { + return mState == TelephonyManager.CALL_STATE_IDLE; + } + + @Override + public void onCallStateChanged(int state) { + mState = state; + if (mRunnable != null) { + mRunnable.run(); + } + } + } } diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java index 22bb5818f9..d36ab616e5 100644 --- a/src/com/android/settings/network/telephony/MobileDataSlice.java +++ b/src/com/android/settings/network/telephony/MobileDataSlice.java @@ -69,7 +69,15 @@ public class MobileDataSlice implements CustomSliceable { public MobileDataSlice(Context context) { mContext = context; mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); - mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + + // Fix: make sure using the same subId when get and set mobile data status + final int defaultSubId = getDefaultSubscriptionId(mSubscriptionManager); + if (SubscriptionManager.INVALID_SUBSCRIPTION_ID != defaultSubId) { + mTelephonyManager = mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(defaultSubId); + } else { + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + } } @Override diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index ba80a8ce65..da22f98b3f 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -18,11 +18,17 @@ package com.android.settings.network.telephony; import android.app.Activity; import android.app.settings.SettingsEnums; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserManager; +import android.provider.SearchIndexableResource; import android.provider.Settings; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -34,6 +40,8 @@ import android.view.MenuItem; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; import com.android.settings.R; import com.android.settings.datausage.BillingCyclePreferenceController; import com.android.settings.datausage.DataUsageSummaryPreferenceController; @@ -49,6 +57,8 @@ import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.utils.ThreadUtils; +import org.codeaurora.internal.IExtTelephony; + import java.util.Arrays; import java.util.List; @@ -61,12 +71,17 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { @VisibleForTesting static final String KEY_CLICKED_PREF = "key_clicked_pref"; + // UICC provisioning status + public static final int CARD_NOT_PROVISIONED = 0; + public static final int CARD_PROVISIONED = 1; + //String keys for preference lookup private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key"; private static final String BUTTON_CDMA_SUBSCRIPTION_KEY = "cdma_subscription_key"; private TelephonyManager mTelephonyManager; private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; private CdmaSystemSelectPreferenceController mCdmaSystemSelectPreferenceController; private CdmaSubscriptionPreferenceController mCdmaSubscriptionPreferenceController; @@ -78,6 +93,33 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { private boolean mDropFirstSubscriptionChangeNotify; private int mActiveSubscriptionsListenerCount; + private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { + String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + Log.d(LOG_TAG, "Received ACTION_SIM_STATE_CHANGED: " + state); + setScreenState(); + } + } + }; + + private void setScreenState() { + int simState = mTelephonyManager.getSimState(); + boolean screenState = simState != TelephonyManager.SIM_STATE_ABSENT; + if (screenState) { + SubscriptionManager subscriptionManager = ((SubscriptionManager) getContext(). + getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)); + SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfo(mSubId); + Log.d(LOG_TAG, "subInfo: " + subInfo + ", mSubId: " + mSubId); + if (subInfo != null && !subInfo.areUiccApplicationsEnabled()) { + screenState = false; + } + } + Log.d(LOG_TAG, "Setting screen state to: " + screenState); + getPreferenceScreen().setEnabled(screenState); + } + public MobileNetworkSettings() { super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); } @@ -117,7 +159,8 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID, MobileNetworkUtils.getSearchableSubscriptionId(context)); - Log.i(LOG_TAG, "display subId: " + mSubId); + mPhoneId = SubscriptionManager.getPhoneId(mSubId); + Log.i(LOG_TAG, "display subId: " + mSubId + ", phoneId: " + mPhoneId); if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { return Arrays.asList(); @@ -136,6 +179,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { if (dataUsageSummaryPreferenceController != null) { dataUsageSummaryPreferenceController.init(mSubId); } + use(DataDefaultSubscriptionController.class).init(getLifecycle()); use(CallsDefaultSubscriptionController.class).init(getLifecycle()); use(SmsDefaultSubscriptionController.class).init(getLifecycle()); use(MobileNetworkSwitchController.class).init(getLifecycle(), mSubId); @@ -154,9 +198,10 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { null /* WifiPickerTrackerCallback */)); use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId); use(ApnPreferenceController.class).init(mSubId); + use(UserPLMNPreferenceController.class).init(mSubId); use(CarrierPreferenceController.class).init(mSubId); use(DataUsagePreferenceController.class).init(mSubId); - use(PreferredNetworkModePreferenceController.class).init(mSubId); + use(PreferredNetworkModePreferenceController.class).init(getLifecycle(), mSubId); use(EnabledNetworkModePreferenceController.class).init(getLifecycle(), mSubId); use(DataServiceSetupPreferenceController.class).init(mSubId); use(Enable2gPreferenceController.class).init(mSubId); @@ -182,6 +227,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { use(VideoCallingPreferenceController.class).init(mSubId); final BackupCallingPreferenceController crossSimCallingPreferenceController = use(BackupCallingPreferenceController.class).init(mSubId); + use(Enabled5GPreferenceController.class).init(mSubId); use(CallingPreferenceCategoryController.class).setChildren( Arrays.asList(wifiCallingPreferenceController, videoCallingPreferenceController, crossSimCallingPreferenceController)); @@ -216,6 +262,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { @Override public void onResume() { + Log.i(LOG_TAG, "onResume:+"); super.onResume(); // TODO: remove log after fixing b/182326102 Log.d(LOG_TAG, "onResume() subId=" + mSubId); @@ -229,6 +276,14 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { mDropFirstSubscriptionChangeNotify = true; } mActiveSubscriptionsListener.start(); + + Context context = getContext(); + if (context != null) { + context.registerReceiver(mSimStateReceiver, + new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED)); + } else { + Log.i(LOG_TAG, "context is null, not registering SimStateReceiver"); + } } private void onSubscriptionDetailChanged() { @@ -256,6 +311,18 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { super.onDestroy(); } + @Override + public void onPause() { + Log.i(LOG_TAG, "onPause:+"); + super.onPause(); + Context context = getContext(); + if (context != null) { + context.unregisterReceiver(mSimStateReceiver); + } else { + Log.i(LOG_TAG, "context already null, not unregistering SimStateReceiver"); + } + } + @VisibleForTesting void onRestoreInstance(Bundle icicle) { if (icicle != null) { @@ -332,6 +399,11 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.mobile_network_settings) { + @Override + public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, + boolean enabled) { + return super.getXmlResourcesToIndex(context, enabled); + } /** suppress full page if user is not admin */ @Override diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java index 0be61e569c..4610b7f4bb 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java @@ -18,10 +18,17 @@ package com.android.settings.network.telephony; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; import android.content.Context; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; +import android.sysprop.TelephonyProperties; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; @@ -42,12 +49,27 @@ public class MobileNetworkSwitchController extends BasePreferenceController impl private int mSubId; private SubscriptionsChangeListener mChangeListener; private SubscriptionManager mSubscriptionManager; + private TelephonyManager mTelephonyManager; + private SubscriptionInfo mSubInfo = null; + private Context mContext; + private int mCallState; + private boolean isReceiverRegistered = false; + public MobileNetworkSwitchController(Context context, String preferenceKey) { super(context, preferenceKey); + mContext = context; mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); mChangeListener = new SubscriptionsChangeListener(context, this); + mTelephonyManager = (TelephonyManager) mContext + .getSystemService(Context.TELEPHONY_SERVICE); + + IntentFilter filter = new IntentFilter(); + filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + mContext.registerReceiver(mIntentReceiver, filter); + isReceiverRegistered = true; + mCallState = mTelephonyManager.getCallState(); } public void init(Lifecycle lifecycle, int subId) { @@ -66,6 +88,14 @@ public class MobileNetworkSwitchController extends BasePreferenceController impl mChangeListener.stop(); } + @OnLifecycleEvent(ON_DESTROY) + public void onDestroy() { + if(isReceiverRegistered) { + mContext.unregisterReceiver(mIntentReceiver); + isReceiverRegistered = false; + } + } + @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); @@ -75,6 +105,8 @@ public class MobileNetworkSwitchController extends BasePreferenceController impl mSwitchBar.setOnBeforeCheckedChangeListener((toggleSwitch, isChecked) -> { // TODO b/135222940: re-evaluate whether to use // mSubscriptionManager#isSubscriptionEnabled + int phoneId = mSubscriptionManager.getSlotIndex(mSubId); + Log.d(TAG, "displayPreference: mSubId=" + mSubId + ", mSubInfo=" + mSubInfo); if (mSubscriptionManager.isActiveSubscriptionId(mSubId) != isChecked) { SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, mSubId, isChecked); return true; @@ -89,21 +121,39 @@ public class MobileNetworkSwitchController extends BasePreferenceController impl return; } - SubscriptionInfo subInfo = null; + if (mTelephonyManager.getActiveModemCount() == 1 && !mSubscriptionManager. + canDisablePhysicalSubscription()) { + Log.d(TAG, "update: Hide SIM option for 1.4 HAL in single sim"); + mSwitchBar.hide(); + return; + } + for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) { if (info.getSubscriptionId() == mSubId) { - subInfo = info; + mSubInfo = info; break; } } + boolean isEcbmEnabled = mTelephonyManager.getEmergencyCallbackMode(); + boolean isScbmEnabled = TelephonyProperties.in_scbm().orElse(false); + if ((TelephonyManager.CALL_STATE_IDLE != mCallState) || isEcbmEnabled || isScbmEnabled) { + Log.d(TAG, "update: disable switchbar, isEcbmEnabled=" + isEcbmEnabled + + ", isScbmEnabled=" + isScbmEnabled + ", mCallState=" + mCallState); + mSwitchBar.setSwitchBarEnabled(false); + } else { + mSwitchBar.setSwitchBarEnabled(true); + } + // For eSIM, we always want the toggle. If telephony stack support disabling a pSIM // directly, we show the toggle. - if (subInfo == null || (!subInfo.isEmbedded() && !SubscriptionUtil.showToggleForPhysicalSim( - mSubscriptionManager))) { + if (mSubInfo == null) { mSwitchBar.hide(); } else { mSwitchBar.show(); + Log.d(TAG, "update(): mSubId=" + mSubId + + ", isActiveSubscriptionId=" + + mSubscriptionManager.isActiveSubscriptionId(mSubId)); mSwitchBar.setCheckedInternal(mSubscriptionManager.isActiveSubscriptionId(mSubId)); } } @@ -122,4 +172,17 @@ public class MobileNetworkSwitchController extends BasePreferenceController impl public void onSubscriptionsChanged() { update(); } + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + mCallState = mTelephonyManager.getCallState(); + Log.d(TAG, "onReceive: mCallState= " + mCallState + ", mSubId=" + mSubId); + update(); + } + } + }; + } diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java index 3a7dc31c10..f4e13712a3 100644 --- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java +++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java @@ -40,6 +40,7 @@ import androidx.preference.Preference; import com.android.internal.telephony.OperatorInfo; import com.android.settings.R; +import com.android.settings.Utils; import java.util.List; import java.util.Objects; @@ -59,7 +60,7 @@ public class NetworkOperatorPreference extends Preference { private List<String> mForbiddenPlmns; private int mLevel = LEVEL_NONE; private boolean mShow4GForLTE; - private boolean mUseNewApi; + private boolean mIsAdvancedScanSupported; public NetworkOperatorPreference(Context context, CellInfo cellinfo, List<String> forbiddenPlmns, boolean show4GForLTE) { @@ -78,8 +79,8 @@ public class NetworkOperatorPreference extends Preference { super(context); mForbiddenPlmns = forbiddenPlmns; mShow4GForLTE = show4GForLTE; - mUseNewApi = context.getResources().getBoolean( - com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI); + mIsAdvancedScanSupported = TelephonyUtils.isAdvancedPlmnScanSupported(context); + Log.d(TAG, "mIsAdvancedScanSupported: " + mIsAdvancedScanSupported); } /** @@ -253,7 +254,7 @@ public class NetworkOperatorPreference extends Preference { } private void updateIcon(int level) { - if (!mUseNewApi || level < 0 || level >= NUM_SIGNAL_STRENGTH_BINS) { + if (!mIsAdvancedScanSupported || level < 0 || level >= NUM_SIGNAL_STRENGTH_BINS) { return; } final Context context = getContext(); diff --git a/src/com/android/settings/network/telephony/NetworkScanHelper.java b/src/com/android/settings/network/telephony/NetworkScanHelper.java index 740b6bba2b..cd686db046 100644 --- a/src/com/android/settings/network/telephony/NetworkScanHelper.java +++ b/src/com/android/settings/network/telephony/NetworkScanHelper.java @@ -17,6 +17,10 @@ package com.android.settings.network.telephony; import android.annotation.IntDef; +import android.content.Context; +import android.content.IntentFilter; +import android.os.RemoteException; +import android.os.ServiceManager; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.CellInfo; import android.telephony.NetworkScan; @@ -29,14 +33,6 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; -import com.android.internal.telephony.CellNetworkScanResult; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -88,20 +84,10 @@ public class NetworkScanHelper { } @Retention(RetentionPolicy.SOURCE) - @IntDef({NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS}) + @IntDef({NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS_LEGACY}) public @interface NetworkQueryType {} /** - * Performs the network scan using {@link TelephonyManager#getAvailableNetworks()}. The network - * scan results won't be returned to the caller until the network scan is completed. - * - * <p> This is typically used when the modem doesn't support the new network scan api - * {@link TelephonyManager#requestNetworkScan( - * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. - */ - public static final int NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS = 1; - - /** * Performs the network scan using {@link TelephonyManager#requestNetworkScan( * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} The network scan * results will be returned to the caller periodically in a small time window until the network @@ -112,7 +98,18 @@ public class NetworkScanHelper { * {@link TelephonyManager#requestNetworkScan( * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} */ - public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2; + public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 1; + + /** + * Performs the network scan using {@link IExtTelephony#performIncrementalScan(int)} + * The network scan is triggered using QcRil hooks, and the results will be returned to the + * caller periodically in a small time window until the network scan is completed. + * + * <p> This is recommended to be used if modem does not support the new network scan api + * {@link TelephonyManager#requestNetworkScan( + * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. + */ + public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS_LEGACY = 2; /** The constants below are used in the async network scan. */ @VisibleForTesting @@ -120,7 +117,7 @@ public class NetworkScanHelper { @VisibleForTesting static final int SEARCH_PERIODICITY_SEC = 5; @VisibleForTesting - static final int MAX_SEARCH_TIME_SEC = 300; + static final int MAX_SEARCH_TIME_SEC = 254; @VisibleForTesting static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3; @@ -128,17 +125,21 @@ public class NetworkScanHelper { private final TelephonyManager mTelephonyManager; private final TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback; private final Executor mExecutor; - + private final LegacyIncrementalScanBroadcastReceiver mLegacyIncrScanReceiver; + private Context mContext; private NetworkScan mNetworkScanRequester; + private IntentFilter filter = + new IntentFilter("qualcomm.intent.action.ACTION_INCREMENTAL_NW_SCAN_IND"); - /** Callbacks for sync network scan */ - private ListenableFuture<List<CellInfo>> mNetworkScanFuture; - - public NetworkScanHelper(TelephonyManager tm, NetworkScanCallback callback, Executor executor) { + public NetworkScanHelper(Context context, TelephonyManager tm, NetworkScanCallback callback, + Executor executor) { + mContext = context; mTelephonyManager = tm; mNetworkScanCallback = callback; mInternalNetworkScanCallback = new NetworkScanCallbackImpl(); mExecutor = executor; + mLegacyIncrScanReceiver = + new LegacyIncrementalScanBroadcastReceiver(mContext, mInternalNetworkScanCallback); } @VisibleForTesting @@ -198,27 +199,8 @@ public class NetworkScanHelper { * @param type used to tell which network scan API should be used. */ public void startNetworkScan(@NetworkQueryType int type) { - if (type == NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS) { - mNetworkScanFuture = SettableFuture.create(); - Futures.addCallback(mNetworkScanFuture, new FutureCallback<List<CellInfo>>() { - @Override - public void onSuccess(List<CellInfo> result) { - onResults(result); - onComplete(); - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof CancellationException) { - return; - } - int errCode = Integer.parseInt(t.getMessage()); - onError(errCode); - } - }, MoreExecutors.directExecutor()); - mExecutor.execute(new NetworkScanSyncTask( - mTelephonyManager, (SettableFuture) mNetworkScanFuture)); - } else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) { + Log.d(TAG, "startNetworkScan: " + type); + if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) { if (mNetworkScanRequester != null) { return; } @@ -230,6 +212,14 @@ public class NetworkScanHelper { Log.d(TAG, "mNetworkScanRequester == null"); onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR); } + } else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS_LEGACY) { + mContext.registerReceiver(mLegacyIncrScanReceiver, filter); + boolean success = TelephonyUtils.performIncrementalScan( + mContext, mTelephonyManager.getSlotIndex()); + Log.d(TAG, "success: " + success); + if (!success) { + onError(NetworkScan.ERROR_RADIO_INTERFACE_ERROR); + } } } @@ -242,11 +232,20 @@ public class NetworkScanHelper { if (mNetworkScanRequester != null) { mNetworkScanRequester.stopScan(); mNetworkScanRequester = null; - } - - if (mNetworkScanFuture != null) { - mNetworkScanFuture.cancel(true /* mayInterruptIfRunning */); - mNetworkScanFuture = null; + } else { + try { + int slotIndex = mTelephonyManager.getSlotIndex(); + if (slotIndex >= 0 && slotIndex < mTelephonyManager.getActiveModemCount()) { + TelephonyUtils.abortIncrementalScan(mContext, slotIndex); + } else { + Log.d(TAG, "slotIndex is invalid, skipping abort"); + } + mContext.unregisterReceiver(mLegacyIncrScanReceiver); + } catch (NullPointerException ex) { + Log.e(TAG, "abortIncrementalScan Exception: ", ex); + } catch (IllegalArgumentException ex) { + Log.e(TAG, "IllegalArgumentException"); + } } } @@ -268,23 +267,6 @@ public class NetworkScanHelper { .anyMatch(i -> i == PhoneCapability.DEVICE_NR_CAPABILITY_SA); } - /** - * Converts the status code of {@link CellNetworkScanResult} to one of the - * {@link NetworkScan.ScanErrorCode}. - * @param errCode status code from {@link CellNetworkScanResult}. - * - * @return one of the scan error code from {@link NetworkScan.ScanErrorCode}. - */ - private static int convertToScanErrorCode(int errCode) { - switch (errCode) { - case CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE: - return NetworkScan.ERROR_RADIO_INTERFACE_ERROR; - case CellNetworkScanResult.STATUS_RADIO_GENERIC_FAILURE: - default: - return NetworkScan.ERROR_MODEM_ERROR; - } - } - private final class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback { public void onResults(List<CellInfo> results) { Log.d(TAG, "Async scan onResults() results = " @@ -302,35 +284,4 @@ public class NetworkScanHelper { NetworkScanHelper.this.onError(errCode); } } - - private static final class NetworkScanSyncTask implements Runnable { - private final SettableFuture<List<CellInfo>> mCallback; - private final TelephonyManager mTelephonyManager; - - NetworkScanSyncTask( - TelephonyManager telephonyManager, SettableFuture<List<CellInfo>> callback) { - mTelephonyManager = telephonyManager; - mCallback = callback; - } - - @Override - public void run() { - final CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks(); - if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) { - final List<CellInfo> cellInfos = result.getOperators() - .stream() - .map(operatorInfo - -> CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo)) - .collect(Collectors.toList()); - Log.d(TAG, "Sync network scan completed, cellInfos = " - + CellInfoUtil.cellInfoListToString(cellInfos)); - mCallback.set(cellInfos); - } else { - final Throwable error = new Throwable( - Integer.toString(convertToScanErrorCode(result.getStatus()))); - mCallback.setException(error); - Log.d(TAG, "Sync network scan error, ex = " + error); - } - } - } } diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index 9faecbb180..6914ede4bc 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -29,6 +29,10 @@ import android.telephony.AccessNetworkConstants; import android.telephony.CarrierConfigManager; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.CellInfoCdma; +import android.telephony.CellInfoGsm; +import android.telephony.CellInfoLte; +import android.telephony.CellInfoWcdma; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -43,7 +47,10 @@ import androidx.preference.PreferenceCategory; import com.android.internal.telephony.OperatorInfo; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.SubscriptionsChangeListener; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; @@ -51,6 +58,8 @@ import com.android.settingslib.utils.ThreadUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.HashSet; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,7 +67,8 @@ import java.util.concurrent.Executors; /** * "Choose network" settings UI for the Settings app. */ -public class NetworkSelectSettings extends DashboardFragment { +public class NetworkSelectSettings extends DashboardFragment implements + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { private static final String TAG = "NetworkSelectSettings"; @@ -81,12 +91,15 @@ public class NetworkSelectSettings extends DashboardFragment { private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @VisibleForTesting TelephonyManager mTelephonyManager; + SubscriptionManager mSubscriptionManager; + private SubscriptionsChangeListener mSubscriptionsChangeListener; private List<String> mForbiddenPlmns; private boolean mShow4GForLTE = false; private NetworkScanHelper mNetworkScanHelper; private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1); private MetricsFeatureProvider mMetricsFeatureProvider; private boolean mUseNewApi; + private boolean mIsAdvancedScanSupported; private long mRequestIdManualNetworkSelect; private long mRequestIdManualNetworkScan; private long mWaitingForNumberOfScanResults; @@ -97,8 +110,13 @@ public class NetworkSelectSettings extends DashboardFragment { public void onCreate(Bundle icicle) { super.onCreate(icicle); - mUseNewApi = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI); + if (TelephonyUtils.isServiceConnected()) { + mIsAdvancedScanSupported = TelephonyUtils.isAdvancedPlmnScanSupported( + getContext()); + } else { + Log.d(TAG, "ExtTelephonyService is not connected!!! "); + } + Log.d(TAG, "mIsAdvancedScanSupported: " + mIsAdvancedScanSupported); Intent intent = getActivity().getIntent(); if (intent != null) { mSubId = intent.getIntExtra(Settings.EXTRA_SUB_ID, @@ -111,8 +129,10 @@ public class NetworkSelectSettings extends DashboardFragment { mSelectedPreference = null; mTelephonyManager = getContext().getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); + mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); + mSubscriptionsChangeListener = new SubscriptionsChangeListener(getContext(), this); mNetworkScanHelper = new NetworkScanHelper( - mTelephonyManager, mCallback, mNetworkScanExecutor); + getContext(), mTelephonyManager, mCallback, mNetworkScanExecutor); PersistableBundle bundle = ((CarrierConfigManager) getContext().getSystemService( Context.CARRIER_CONFIG_SERVICE)).getConfigForSubId(mSubId); if (bundle != null) { @@ -138,18 +158,21 @@ public class NetworkSelectSettings extends DashboardFragment { .findViewById(R.id.progress_bar_animation); setProgressBarVisible(false); } - forceUpdateConnectedPreferenceCategory(); } @Override public void onStart() { + Log.d(TAG, "onStart()"); super.onStart(); + mSubscriptionsChangeListener.start(); updateForbiddenPlmns(); if (isProgressBarVisible()) { return; } if (mWaitingForNumberOfScanResults <= 0) { + // Clear the selected preference whenever the scan starts + mSelectedPreference = null; startNetworkQuery(); } } @@ -167,6 +190,8 @@ public class NetworkSelectSettings extends DashboardFragment { @Override public void onStop() { + Log.d(TAG, "onStop() mWaitingForNumberOfScanResults: " + mWaitingForNumberOfScanResults); + mSubscriptionsChangeListener.stop(); super.onStop(); if (mWaitingForNumberOfScanResults <= 0) { stopNetworkQuery(); @@ -211,6 +236,27 @@ public class NetworkSelectSettings extends DashboardFragment { } @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + } + + @Override + public void onSubscriptionsChanged() { + boolean isActiveSubscriptionId = mSubscriptionManager.isActiveSubscriptionId(mSubId); + Log.d(TAG, "onSubscriptionsChanged, mSubId: " + mSubId + + ", isActive: " + isActiveSubscriptionId); + + if (!isActiveSubscriptionId) { + // The current subscription is no longer active, possibly because the SIM was removed. + // Finish the activity. + final Activity activity = getActivity(); + if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) { + Log.d(TAG, "Calling finish"); + activity.finish(); + } + } + } + + @Override protected int getPreferenceScreenResId() { return R.xml.choose_network; } @@ -228,10 +274,10 @@ public class NetworkSelectSettings extends DashboardFragment { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { + Log.d(TAG, "handleMessage, msg.what: " + msg.what); switch (msg.what) { case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE: final boolean isSucceed = (boolean) msg.obj; - stopNetworkQuery(); setProgressBarVisible(false); getPreferenceScreen().setEnabled(true); @@ -256,6 +302,7 @@ public class NetworkSelectSettings extends DashboardFragment { } mCellInfoList = doAggregation(results); + Log.d(TAG, "CellInfoList size: " + mCellInfoList.size()); Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); if (mCellInfoList != null && mCellInfoList.size() != 0) { final NetworkOperatorPreference connectedPref = @@ -464,8 +511,22 @@ public class NetworkSelectSettings extends DashboardFragment { if (mForbiddenPlmns == null) { updateForbiddenPlmns(); } + + Set<CellIdentity> cellIdentitySet = new HashSet<CellIdentity>(); for (NetworkRegistrationInfo regInfo : networkList) { + Log.d(TAG, "regInfo: " + regInfo.toString()); + // There can be multiple NetworkRegistrationInfos for the same CellIdentity, + // e.g., one each for CS and PS. In such cases, show show only one entry, + // otherwise it would be quite confusing to the user to see multiple entries for + // the same CellIdentity instance. final CellIdentity cellIdentity = regInfo.getCellIdentity(); + if (cellIdentity != null) { + // Add each valid CellIdentity to a HashSet so that only unique values remain. + cellIdentitySet.add(cellIdentity); + } + } + + for (CellIdentity cellIdentity : cellIdentitySet) { if (cellIdentity == null) { continue; } @@ -480,7 +541,6 @@ public class NetworkSelectSettings extends DashboardFragment { // (it would be quite confusing why the connected network has no signal) pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1); mPreferenceCategory.addPreference(pref); - break; } } } @@ -528,10 +588,11 @@ public class NetworkSelectSettings extends DashboardFragment { if (mNetworkScanHelper != null) { mRequestIdManualNetworkScan = getNewRequestId(); mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED; + mNetworkScanHelper.startNetworkScan( - mUseNewApi + mIsAdvancedScanSupported ? NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS - : NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS); + : NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS_LEGACY); } } @@ -545,6 +606,7 @@ public class NetworkSelectSettings extends DashboardFragment { @Override public void onDestroy() { + Log.d(TAG, "onDestroy()"); stopNetworkQuery(); mNetworkScanExecutor.shutdown(); super.onDestroy(); diff --git a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java index 24197795fa..31b9de524b 100644..100755 --- a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java @@ -16,28 +16,49 @@ package com.android.settings.network.telephony; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; import android.os.PersistableBundle; +import android.provider.Settings; import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.util.Log; +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.ListPreference; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.network.AllowedNetworkTypesListener; import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants; /** * Preference controller for "Preferred network mode" */ public class PreferredNetworkModePreferenceController extends TelephonyBasePreferenceController - implements ListPreference.OnPreferenceChangeListener { + implements ListPreference.OnPreferenceChangeListener, LifecycleObserver { + private static final String LOG_TAG = "PreferredNetworkMode"; private CarrierConfigManager mCarrierConfigManager; private TelephonyManager mTelephonyManager; private PersistableBundle mPersistableBundle; private boolean mIsGlobalCdma; + private Preference mPreference; + private PhoneCallStateListener mPhoneStateListener; + private AllowedNetworkTypesListener mAllowedNetworkTypesListener; + @VisibleForTesting + Integer mCallState; public PreferredNetworkModePreferenceController(Context context, String key) { super(context, key); @@ -66,6 +87,33 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } + @OnLifecycleEvent(ON_START) + public void onStart() { + if (mPhoneStateListener != null) { + mPhoneStateListener.register(mContext, mSubId); + } + if (mAllowedNetworkTypesListener != null) { + mAllowedNetworkTypesListener.register(mContext, mSubId); + } + + } + + @OnLifecycleEvent(ON_STOP) + public void onStop() { + if (mPhoneStateListener != null) { + mPhoneStateListener.unregister(); + } + if (mAllowedNetworkTypesListener != null) { + mAllowedNetworkTypesListener.unregister(mContext, mSubId); + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + @Override public void updateState(Preference preference) { super.updateState(preference); @@ -73,6 +121,7 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe final int networkMode = getPreferredNetworkMode(); listPreference.setValue(Integer.toString(networkMode)); listPreference.setSummary(getPreferredNetworkModeSummaryResId(networkMode)); + listPreference.setEnabled(isCallStateIdle()); } @Override @@ -83,19 +132,37 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, MobileNetworkUtils.getRafFromNetworkType(newPreferredNetworkMode)); - final ListPreference listPreference = (ListPreference) preference; - listPreference.setSummary(getPreferredNetworkModeSummaryResId(newPreferredNetworkMode)); - return true; + final ListPreference listPreference = (ListPreference) preference; + listPreference.setSummary(getPreferredNetworkModeSummaryResId(newPreferredNetworkMode)); + return true; } - public void init(int subId) { + public void init(Lifecycle lifecycle, int subId) { mSubId = subId; + if (mPhoneStateListener == null) { + mPhoneStateListener = new PhoneCallStateListener(); + } final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); mIsGlobalCdma = mTelephonyManager.isLteCdmaEvdoGsmWcdmaEnabled() && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL); + + if (mAllowedNetworkTypesListener == null) { + mAllowedNetworkTypesListener = new AllowedNetworkTypesListener( + mContext.getMainExecutor()); + mAllowedNetworkTypesListener.setAllowedNetworkTypesListener( + () -> updatePreference()); + } + + lifecycle.addObserver(this); + } + + private void updatePreference() { + if (mPreference != null) { + updateState(mPreference); + } } private int getPreferredNetworkMode() { @@ -186,4 +253,42 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe return R.string.preferred_network_mode_global_summary; } } + + private boolean isCallStateIdle() { + boolean callStateIdle = true; + if (mCallState != null && mCallState != TelephonyManager.CALL_STATE_IDLE) { + callStateIdle = false; + } + Log.d(LOG_TAG, "isCallStateIdle:" + callStateIdle); + return callStateIdle; + } + + private class PhoneCallStateListener extends PhoneStateListener { + + PhoneCallStateListener() { + super(Looper.getMainLooper()); + } + + private TelephonyManager mTelephonyManager; + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + mCallState = state; + updateState(mPreference); + } + + public void register(Context context, int subId) { + mTelephonyManager = context.getSystemService(TelephonyManager.class); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); + } + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); + + } + + public void unregister() { + mCallState = null; + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); + } + } } diff --git a/src/com/android/settings/network/telephony/TelephonyUtils.java b/src/com/android/settings/network/telephony/TelephonyUtils.java new file mode 100644 index 0000000000..a759972b55 --- /dev/null +++ b/src/com/android/settings/network/telephony/TelephonyUtils.java @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.settings.network.telephony; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import com.qti.extphone.ExtTelephonyManager; +import com.qti.extphone.QtiImeiInfo; +import com.qti.extphone.ServiceCallback; + +import org.codeaurora.internal.IExtTelephony; +import java.util.Optional; + +/** + * Add static utility functions to get information about Primary Card and Subsidy Lock features. + */ +public final class TelephonyUtils { + + private static final String TAG = "TelephonyUtils"; + + // Flag to control debug logging for primary card and subsidy lock features + public static boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String PROPERTY_ADVANCED_SCAN = "persist.vendor.radio.enableadvancedscan"; + + private static final String PROPERTY_SUBSIDY_DEVICE = "persist.vendor.radio.subsidydevice"; + private static final String ALLOW_USER_SELECT_DDS = "allow_user_select_dds"; + + // Modem version prefix tag + private static final String MODEM_VERSION_PREFIX_HI_TAG = "MPSS.HI."; // Himalaya + private static final String MODEM_VERSION_PREFIX_DE_TAG = "MPSS.DE."; // Denali + + // UICC provisioning status + public static final int CARD_NOT_PROVISIONED = 0; + public static final int CARD_PROVISIONED = 1; + public static final int CARD_INVALID_STATE = -1; + + private static ExtTelephonyManager mExtTelephonyManager; + private static boolean mIsServiceBound; + private static Optional<Boolean> mIsSubsidyFeatureEnabled = Optional.empty(); + + private TelephonyUtils() { + } + + public static boolean isAdvancedPlmnScanSupported(Context context) { + boolean propVal = false; + if (mIsServiceBound) { + try { + propVal = mExtTelephonyManager.getPropertyValueBool(PROPERTY_ADVANCED_SCAN, false); + } catch (NullPointerException ex) { + Log.e(TAG, "isAdvancedPlmnScanSupported: , Exception: ", ex); + } + } else { + Log.e(TAG, "isAdvancedPlmnScanSupported: ExtTelephony Service not connected!"); + } + return propVal; + } + + public static boolean performIncrementalScan(Context context, int slotId) { + boolean success = false; + if (mIsServiceBound) { + success = mExtTelephonyManager.performIncrementalScan(slotId); + } else { + Log.e(TAG, "performIncrementalScan: ExtTelephony Service not connected!"); + } + return success; + } + + public static void abortIncrementalScan(Context context, int slotId) { + if (mIsServiceBound) { + mExtTelephonyManager.abortIncrementalScan(slotId); + } else { + Log.e(TAG, "abortIncrementalScan: ExtTelephony Service not connected!"); + } + } + + /* + * As many products come from different modem version, it is hard to maintain one + * carrier config along with vendor product SKU. But MPSS version code is stable + * very much, it is a good way rather than config's approach. + */ + public static boolean isDual5gSupported(TelephonyManager telephonyManager) { + if (telephonyManager == null) { + Log.e(TAG, "telephonyManager is null"); + return false; + } + final String version = telephonyManager.getBasebandVersion(); + Log.d(TAG, "Base band version = " + version); + if (!TextUtils.isEmpty(version)) { + String[] tokens = version.split("-"); + if (tokens != null) { + for (String token : tokens) { + if (token != null && token.startsWith(MODEM_VERSION_PREFIX_HI_TAG)) { + String verCode = + token.substring(MODEM_VERSION_PREFIX_HI_TAG.length(), + token.length()); + Log.d(TAG, "verCode = " + verCode); + if (verCode != null && verCode.length() > 2) { + String[] subCode = verCode.split("\\."); + try { + int major = Integer.parseInt(subCode[0]); + int minor = Integer.parseInt(subCode[1]); + Log.d(TAG, "Ver major = " + major + " minor = " + minor); + if (major >= 4 && minor >= 3) { + return true; + } + } catch (NumberFormatException e) { + Log.e(TAG, "Fail to parse version"); + return false; + } + } + } else if (token != null && token.startsWith(MODEM_VERSION_PREFIX_DE_TAG)) { + return true; + } + } + } + } + return false; + } + + public static boolean isSubsidyFeatureEnabled(Context context) { + if (!mIsSubsidyFeatureEnabled.isPresent()) { + if (!mIsServiceBound) { + Log.e(TAG, "isSubsidyFeatureEnabled: ExtTelephony Service not connected!"); + connectExtTelephonyService(context); + } + + try { + mIsSubsidyFeatureEnabled = + Optional.of(mExtTelephonyManager.getPropertyValueBool( + PROPERTY_SUBSIDY_DEVICE, false)); + } catch (NullPointerException ex) { + Log.e(TAG, "isSubsidyFeatureEnabled: , Exception: ", ex); + } + } + return mIsSubsidyFeatureEnabled.get(); + } + + public static boolean allowUsertoSetDDS(Context context) { + return Settings.Global.getInt(context.getContentResolver(), ALLOW_USER_SELECT_DDS, 0) == 1; + } + + public static boolean isSubsidySimCard(Context context, int slotId) { + boolean isSubsidySim = false; + if (!mIsServiceBound) { + Log.e(TAG, "isSubsidySimCard: ExtTelephony Service not connected!"); + connectExtTelephonyService(context); + } + + try { + isSubsidySim = mExtTelephonyManager.isPrimaryCarrierSlotId(slotId); + } catch (NullPointerException ex) { + Log.e(TAG, "isSubsidySimCard: , Exception: ", ex); + } + return isSubsidySim; + } + + public static QtiImeiInfo[] getImeiInfo() { + QtiImeiInfo[] qtiImeiInfo = null; + if (isServiceConnected()) { + qtiImeiInfo = mExtTelephonyManager.getImeiInfo(); + } else { + Log.e(TAG, "getImeiInfo: ExtTelephony Service not connected!"); + } + return qtiImeiInfo; + } + + public static void connectExtTelephonyService(Context context) { + if (!mIsServiceBound) { + Log.d(TAG, "Connect to ExtTelephonyService..."); + mExtTelephonyManager = ExtTelephonyManager.getInstance(context); + mExtTelephonyManager.connectService(mServiceCallback); + } + } + + public static boolean isServiceConnected() { + return mIsServiceBound; + } + + private static ServiceCallback mServiceCallback = new ServiceCallback() { + @Override + public void onConnected() { + Log.d(TAG, "ExtTelephony Service connected"); + mIsServiceBound = true; + } + + @Override + public void onDisconnected() { + Log.d(TAG, "ExtTelephony Service disconnected..."); + mIsServiceBound = false; + } + }; +} diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java index 0064e6ccfd..7baadd3a8e 100644 --- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java +++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java @@ -292,7 +292,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc } private void handleTogglePsimAction() { - if (mSubscriptionManager.canDisablePhysicalSubscription() && mSubInfo != null) { + if (mSubInfo != null) { mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable); finish(); } else { diff --git a/src/com/android/settings/network/telephony/UserPLMNEditorActivity.java b/src/com/android/settings/network/telephony/UserPLMNEditorActivity.java new file mode 100755 index 0000000000..dfac3fa180 --- /dev/null +++ b/src/com/android/settings/network/telephony/UserPLMNEditorActivity.java @@ -0,0 +1,406 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.settings.network.telephony; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.text.InputType; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.EditText; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.os.Bundle; +import android.util.Log; + +import com.android.internal.telephony.TelephonyIntents; +import com.android.settings.R; + +import java.util.List; + +/** + * Editor activity for "User Controlled PLMN" + */ +public class UserPLMNEditorActivity extends PreferenceActivity implements + Preference.OnPreferenceChangeListener, TextWatcher { + private static final String LOG_TAG = "UserPLMNEditorActivity"; + private static final int MENU_DELETE_OPTION = Menu.FIRST; + private static final int MENU_SAVE_OPTION = Menu.FIRST + 1; + private static final int MENU_CANCEL_OPTION = Menu.FIRST + 2; + private static final int NWID_DIALOG_ID = 0; + + private static final String BUTTON_NETWORK_ID_KEY = "network_id_key"; + private static final String BUTTON_PRIORITY_KEY = "priority_key"; + private static final String BUTTON_NEWWORK_MODE_KEY = "network_mode_key"; + + public static final String UPLMN_CODE = "uplmn_code"; + public static final String UPLMN_PRIORITY = "uplmn_priority"; + public static final String UPLMN_SERVICE = "uplmn_service"; + public static final String UPLMN_SIZE = "uplmn_size"; + public static final String UPLMN_ADD = "uplmn_add"; + + public static final int RESULT_CODE_EDIT = 101; + public static final int RESULT_CODE_DELETE = 102; + + private static final int GSM = 0; + private static final int WCDMA_TDSCDMA = 1; + private static final int LTE = 2; + private static final int TRIPLE_MODE = 3; + private static final int MODE_2G = 0x1; + private static final int MODE_3G = 0x4; + private static final int MODE_LTE = 0x8; + private static final int MODE_TRIPLE = 0xd; + + private String mNoSet = null; + private boolean mAirplaneModeOn = false; + private IntentFilter mIntentFilter; + private Preference mNWIDPref = null; + private EditTextPreference mPRIpref = null; + private ListPreference mNWMPref = null; + private EditText mNWIDText; + private AlertDialog mNWIDDialog = null; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + mAirplaneModeOn = intent.getBooleanExtra("state", false); + setScreenEnabled(); + } + } + }; + + private OnClickListener mNWIDPrefListener = new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + String summary = genText(mNWIDText.getText().toString()); + Log.d(LOG_TAG, "input network id is " + summary); + mNWIDPref.setSummary(summary); + mNWMPref.setEntries(getResources().getTextArray( + selectNetworkChoices(summary))); + } + } + }; + + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.uplmn_editor); + mNoSet = getResources().getString(R.string.voicemail_number_not_set); + + mNWIDPref = (Preference) findPreference(BUTTON_NETWORK_ID_KEY); + mPRIpref = (EditTextPreference) findPreference(BUTTON_PRIORITY_KEY); + mNWMPref = (ListPreference) findPreference(BUTTON_NEWWORK_MODE_KEY); + + mPRIpref.setOnPreferenceChangeListener(this); + mNWMPref.setOnPreferenceChangeListener(this); + + mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + registerReceiver(mReceiver, mIntentFilter); + } + + protected void onResume() { + super.onResume(); + displayNetworkInfo(getIntent()); + mAirplaneModeOn = + Settings.System.getInt( + getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + setScreenEnabled(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } + + public boolean onPreferenceChange(Preference preference, Object object) { + String value = object.toString(); + if (preference == mPRIpref) { + mPRIpref.setSummary(genText(value)); + } else if (preference == mNWMPref) { + mNWMPref.setValue(value); + String summary = ""; + int index = Integer.parseInt(value); + summary = getResources().getStringArray( + selectNetworkChoices(mNWIDPref.getSummary().toString()))[index]; + mNWMPref.setSummary(summary); + } + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + if (!getIntent().getBooleanExtra(UPLMN_ADD, false)) { + menu.add(0, MENU_DELETE_OPTION, 0, + com.android.internal.R.string.delete); + } + menu.add(0, MENU_SAVE_OPTION, 0, R.string.save); + menu.add(0, MENU_CANCEL_OPTION, 0, com.android.internal.R.string.cancel); + return true; + } + + @Override + public boolean onMenuOpened(int featureId, Menu menu) { + super.onMenuOpened(featureId, menu); + boolean isEmpty = mNoSet.equals(mNWIDPref.getSummary()) + || mNoSet.equals(mPRIpref.getSummary()); + if (menu != null) { + menu.setGroupEnabled(0, !mAirplaneModeOn); + //only show the save and delete option menu when radio on + //and edit text is not empty. + if (getIntent().getBooleanExtra(UPLMN_ADD, true)) { + menu.getItem(0).setEnabled((!mAirplaneModeOn) && !isEmpty); + } else { + menu.getItem(1).setEnabled((!mAirplaneModeOn) && !isEmpty); + } + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_SAVE_OPTION: + setSavedNWInfo(); + break; + case MENU_DELETE_OPTION: + setRemovedNWInfo(); + break; + case MENU_CANCEL_OPTION: + break; + case android.R.id.home: + finish(); + return true; + default: + break; + } + finish(); + return super.onOptionsItemSelected(item); + } + + private void setSavedNWInfo() { + Intent intent = new Intent(this, UserPLMNListActivity.class); + genNWInfoToIntent(intent); + setResult(RESULT_CODE_EDIT, intent); + } + + private void genNWInfoToIntent(Intent intent) { + int priority = 0; + int plmnListSize = getIntent().getIntExtra(UPLMN_SIZE, 0); + try { + priority = Integer.parseInt(String.valueOf(mPRIpref.getSummary())); + } catch (NumberFormatException e) { + Log.d(LOG_TAG, "parse value of basband error"); + } + if (getIntent().getBooleanExtra(UPLMN_ADD, false)) { + if (priority > plmnListSize) { + priority = plmnListSize; + } + } else { + if (priority >= plmnListSize) { + priority = plmnListSize - 1; + } + } + intent.putExtra(UPLMN_PRIORITY, priority); + + try { + intent.putExtra(UPLMN_SERVICE, convertApMode2EF(Integer + .parseInt(String.valueOf(mNWMPref.getValue())))); + } catch (NumberFormatException e) { + intent.putExtra(UPLMN_SERVICE, convertApMode2EF(0)); + } + + intent.putExtra(UPLMN_CODE, mNWIDPref.getSummary()); + } + + private void setRemovedNWInfo() { + Intent intent = new Intent(this, UserPLMNListActivity.class); + genNWInfoToIntent(intent); + setResult(RESULT_CODE_DELETE, intent); + } + + public static int convertEFMode2Ap(int mode) { + int result = 0; + if (mode == MODE_TRIPLE) { + result = TRIPLE_MODE; + } else if (mode == MODE_3G) { + result = WCDMA_TDSCDMA; + } else if (mode == MODE_LTE) { + result = LTE; + } else { + result = GSM; + } + return result; + } + + public static int convertApMode2EF(int mode) { + int result = 0; + if (mode == TRIPLE_MODE) { + result = MODE_TRIPLE; + } else if (mode == LTE) { + result = MODE_LTE; + } else if (mode == WCDMA_TDSCDMA) { + result = MODE_3G; + } else { + result = MODE_2G; + } + return result; + } + + private void displayNetworkInfo(Intent intent) { + String number = intent.getStringExtra(UPLMN_CODE); + mNWIDPref.setSummary(genText(number)); + int priority = intent.getIntExtra(UPLMN_PRIORITY, 0); + mPRIpref.setSummary(String.valueOf(priority)); + mPRIpref.setText(String.valueOf(priority)); + int act = intent.getIntExtra(UPLMN_SERVICE, 0); + + Log.d(LOG_TAG, "act = " + act); + + act = convertEFMode2Ap(act); + if (act < GSM || act > TRIPLE_MODE) { + act = GSM; + } + String summary = ""; + mNWMPref.setEntries(getResources().getTextArray( + selectNetworkChoices(number))); + summary = getResources().getStringArray( + selectNetworkChoices(number))[act]; + mNWMPref.setSummary(summary); + mNWMPref.setValue(String.valueOf(act)); + } + + public int selectNetworkChoices(String plmn) { + Log.d(LOG_TAG, "plmn = " + plmn); + String[] CuPlmnArray = getResources().getStringArray(R.array.uplmn_cu_mcc_mnc_values); + for (String CuPlmn : CuPlmnArray) { + if (!TextUtils.isEmpty(plmn) && plmn.equals(CuPlmn)) { + return R.array.uplmn_prefer_network_mode_w_choices; + } + } + return R.array.uplmn_prefer_network_mode_td_choices; + } + + private String genText(String value) { + if (value == null || value.length() == 0) { + return mNoSet; + } else { + return value; + } + } + + public void buttonEnabled() { + int len = mNWIDText.getText().toString().length(); + boolean state = true; + if (len <5 || len > 6) { + state = false; + } + if (mNWIDDialog != null) { + mNWIDDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled( + state); + } + } + + private void setScreenEnabled() { + getPreferenceScreen().setEnabled(!mAirplaneModeOn); + invalidateOptionsMenu(); + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen screen, + Preference preference) { + if (preference == mNWIDPref) { + removeDialog(NWID_DIALOG_ID); + showDialog(NWID_DIALOG_ID); + buttonEnabled(); + } + return super.onPreferenceTreeClick(screen, preference); + } + + @Override + public Dialog onCreateDialog(int id) { + if (id == NWID_DIALOG_ID) { + mNWIDText = new EditText(this); + if (!mNoSet.equals(mNWIDPref.getSummary())) { + mNWIDText.setText(mNWIDPref.getSummary()); + } + mNWIDText.addTextChangedListener(this); + mNWIDText.setInputType(InputType.TYPE_CLASS_NUMBER); + mNWIDDialog = new AlertDialog.Builder(this) + .setTitle(getResources().getString(R.string.network_id)) + .setView(mNWIDText) + .setPositiveButton( + getResources().getString( + com.android.internal.R.string.ok), + mNWIDPrefListener) + .setNegativeButton( + getResources().getString( + com.android.internal.R.string.cancel), null) + .create(); + mNWIDDialog.getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + return mNWIDDialog; + } + return null; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + } + + @Override + public void afterTextChanged(Editable s) { + buttonEnabled(); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + +} diff --git a/src/com/android/settings/network/telephony/UserPLMNListActivity.java b/src/com/android/settings/network/telephony/UserPLMNListActivity.java new file mode 100755 index 0000000000..a49ce87e43 --- /dev/null +++ b/src/com/android/settings/network/telephony/UserPLMNListActivity.java @@ -0,0 +1,748 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.settings.network.telephony; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncResult; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.util.Log; + +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.uicc.IccConstants; +import com.android.internal.telephony.uicc.IccUtils; +import com.android.internal.telephony.uicc.PlmnActRecord; +import com.android.internal.telephony.uicc.UiccController; +import com.android.settings.R; + +import org.codeaurora.internal.IExtTelephony; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * List the MCCMNC and the priority for "User Controlled PLMN" + */ +public class UserPLMNListActivity extends PreferenceActivity + implements DialogInterface.OnCancelListener { + private static final String LOG_TAG = "UserPLMNListActivity"; + private static final boolean DBG = true; + + private static final int BUSY_READING_DIALOG = 99; + private static final int BUSY_SAVING_DIALOG = 100; + private static final int UPLMNLIST_ADD = 101; + private static final int UPLMNLIST_EDIT = 102; + private static final int MENU_ADD_OPTIION = Menu.FIRST; + + /* 5n bytes: + 1 to 3 nth PLMN (highest priority) + 4 to 5 nth PLMN Access Technology Identifier */ + private static final int UPLMN_SEL_DATA_LEN = 5; + private static final int GSM_MASK = 1; + /** + * GSM compact access technology. + */ + private static final int GSM_COMPACT_MASK = 2; + /** + * UMTS radio access technology. + */ + private static final int UMTS_MASK = 4; + /** + * LTE radio access technology. + */ + private static final int LTE_MASK = 8; + + private List<UPLMNInfoWithEf> mUPLMNList; + private PreferenceScreen mUPLMNListContainer; + private static final String BUTTON_UPLMN_LIST_KEY = "button_uplmn_list_key"; + private Map<Preference, UPLMNInfoWithEf> mPreferenceMap + = new LinkedHashMap<Preference, UPLMNInfoWithEf>(); + private UPLMNInfoWithEf mOldInfo; + private int mNumRec = 0; + private boolean mAirplaneModeOn = false; + private int mPhoneId = 0; + protected boolean mIsForeground = false; + + private MyHandler mHandler = new MyHandler(); + private IExtTelephony mExtTelephony; + private static final String ACTION_READ_EF_BROADCAST = + "com.qualcomm.qti.intent.action.ACTION_READ_EF_RESULT"; + private static final String ACTION_WRITE_EF_BROADCAST = + "com.qualcomm.qti.intent.action.ACTION_WRITE_EF_RESULT"; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + mAirplaneModeOn = intent.getBooleanExtra("state", false); + setScreenEnabled(); + } else if (ACTION_READ_EF_BROADCAST.equals(action)) { + if (intent.getBooleanExtra("exception", false)) { + log("ACTION_READ_EF_BROADCAST with exception"); + Message message = mHandler.obtainMessage(); + message.what = mHandler.MESSAGE_GET_UPLMN_LIST; + message.obj = new AsyncResult(null, null, new Exception()); + // trigger tone stop after timeout duration + mHandler.sendMessage(message); + } else { + handleGetEFDone(intent.getByteArrayExtra("payload")); + } + } else if (ACTION_WRITE_EF_BROADCAST.equals(action)) { + if (intent.getBooleanExtra("exception", false)) { + log("ACTION_WRITE_EF_BROADCAST with exception"); + } else { + log("handleSetEFDone: with OK result!"); + } + getUPLMNInfoFromEf(); + } + } + }; + + @Override + protected Dialog onCreateDialog(int id) { + ProgressDialog dialog = new ProgressDialog(this); + dialog.setTitle(getText(R.string.uplmn_settings_title)); + dialog.setIndeterminate(true); + switch(id) { + case BUSY_READING_DIALOG: + dialog.setCancelable(true); + dialog.setOnCancelListener(this); + dialog.setMessage(getText(R.string.reading_settings)); + return dialog; + case BUSY_SAVING_DIALOG: + dialog.setCancelable(false); + dialog.setMessage(getText(R.string.updating_settings)); + return dialog; + } + return null; + } + + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.uplmn_settings); + mUPLMNListContainer = (PreferenceScreen) findPreference(BUTTON_UPLMN_LIST_KEY); + int subId = getIntent().getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mPhoneId = SubscriptionManager.getPhoneId(subId); + + mExtTelephony + = IExtTelephony.Stub + .asInterface(ServiceManager.getService("qti.radio.extphone")); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intentFilter.addAction(ACTION_WRITE_EF_BROADCAST); + intentFilter.addAction(ACTION_READ_EF_BROADCAST); + registerReceiver(mReceiver, intentFilter); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } + + public void onResume() { + super.onResume(); + mIsForeground = true; + getUPLMNInfoFromEf(); + showReadingDialog(); + mAirplaneModeOn = Settings.System.getInt(getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + @Override + public void onPause() { + super.onPause(); + mIsForeground = false; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(0, MENU_ADD_OPTIION, 0, R.string.uplmn_list_setting_add_plmn) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (menu != null) { + menu.setGroupEnabled(0, !mAirplaneModeOn); + } + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ADD_OPTIION: + if (!hasGetIccFileHandler()) return true; + Intent intent = new Intent(this, UserPLMNEditorActivity.class); + intent.putExtra(UserPLMNEditorActivity.UPLMN_CODE, ""); + intent.putExtra(UserPLMNEditorActivity.UPLMN_PRIORITY, 0); + intent.putExtra(UserPLMNEditorActivity.UPLMN_SERVICE, 0); + intent.putExtra(UserPLMNEditorActivity.UPLMN_ADD, true); + intent.putExtra(UserPLMNEditorActivity.UPLMN_SIZE, mUPLMNList.size()); + startActivityForResult(intent, UPLMNLIST_ADD); + break; + case android.R.id.home: + finish(); + return true; + default: + break; + } + return super.onOptionsItemSelected(item); + } + + private void showReadingDialog() { + if (mIsForeground && hasGetIccFileHandler()) { + showDialog(BUSY_READING_DIALOG); + } + } + + private void showSavingDialog() { + if (mIsForeground) { + showDialog(BUSY_SAVING_DIALOG); + } + } + + private void dismissDialogSafely(int id) { + try { + dismissDialog(id); + } catch (IllegalArgumentException e) { + // This is expected in the case where we were in the background + // at the time we would normally have shown the dialog, so we didn't + // show it. + } + } + + public void onFinished(Preference preference, boolean reading) { + log("onFinished reading: " + reading); + if (reading) { + dismissDialogSafely(BUSY_READING_DIALOG); + } else { + dismissDialogSafely(BUSY_SAVING_DIALOG); + } + preference.setEnabled(true); + setScreenEnabled(); + } + + private void getUPLMNInfoFromEf() { + log("UPLMNInfoFromEf Start read..."); + if (!readEfFromIcc(IccConstants.EF_PLMN_W_ACT)) { + Log.w(LOG_TAG, "mIccFileHandler is null"); + } + } + + private boolean hasGetIccFileHandler() { + boolean success = false; + try { + if (mExtTelephony != null) { + success = mExtTelephony.hasGetIccFileHandler(mPhoneId, UiccController.APP_FAM_3GPP); + } + } catch (RemoteException | NullPointerException ex) { + Log.e(LOG_TAG, "hasGetIccFileHandler Exception: ", ex); + + } + return success; + } + + private boolean readEfFromIcc(int efid) { + boolean success = false; + try { + if (mExtTelephony != null) { + success = mExtTelephony.readEfFromIcc(mPhoneId, UiccController.APP_FAM_3GPP, efid); + } + } catch (RemoteException | NullPointerException ex) { + Log.e(LOG_TAG, "readEfFromIcc Exception: ", ex); + + } + return success; + } + + private boolean writeEfToIcc(byte[] efdata, int efid) { + boolean success = false; + try { + if (mExtTelephony != null) { + success = mExtTelephony.writeEfToIcc(mPhoneId, UiccController.APP_FAM_3GPP, + efid, efdata); + } + } catch (RemoteException | NullPointerException ex) { + Log.e(LOG_TAG, "writeEfToIcc Exception: ", ex); + + } + return success; + } + + private void refreshUPLMNListPreference(ArrayList<UPLMNInfoWithEf> list) { + if (mUPLMNListContainer.getPreferenceCount() != 0) { + mUPLMNListContainer.removeAll(); + } + + if (this.mPreferenceMap != null) { + mPreferenceMap.clear(); + } + + if (mUPLMNList != null) { + mUPLMNList.clear(); + } + mUPLMNList = list; + if (list == null) { + log("refreshUPLMNListPreference : NULL UPLMN list!"); + } else { + log("refreshUPLMNListPreference : list.size()" + list.size()); + } + + if (list == null || list.size() == 0) { + if (DBG) { + log("refreshUPLMNListPreference : NULL UPLMN list!"); + } + if (list == null) { + mUPLMNList = new ArrayList<UPLMNInfoWithEf>(); + } + return; + } + + for (UPLMNInfoWithEf network : list) { + addUPLMNPreference(network); + if (DBG) { + log(network.toString()); + } + } + } + + class UPLMNInfoWithEf { + + private String mOperatorNumeric; + + private int mNetworkMode; + private int mPriority; // priority is the index of the plmn in the list. + + public String getOperatorNumeric() { + return mOperatorNumeric; + } + + public int getNetworMode() { + return mNetworkMode; + } + + public int getPriority() { + return mPriority; + } + + public void setOperatorNumeric(String operatorNumeric) { + this.mOperatorNumeric = operatorNumeric; + } + + public void setPriority(int index) { + this.mPriority = index; + } + + public UPLMNInfoWithEf(String operatorNumeric, int mNetworkMode, + int mPriority) { + this.mOperatorNumeric = operatorNumeric; + this.mNetworkMode = mNetworkMode; + this.mPriority = mPriority; + } + + public String toString() { + return "UPLMNInfoWithEf " + mOperatorNumeric + "/" + mNetworkMode + + "/" + mPriority; + } + } + + class PriorityCompare implements Comparator<UPLMNInfoWithEf> { + + public int compare(UPLMNInfoWithEf object1, UPLMNInfoWithEf object2) { + return (object1.getPriority() - object2.getPriority()); + } + } + + private void addUPLMNPreference(UPLMNInfoWithEf network) { + Preference pref = new Preference(this); + String plmnName = network.getOperatorNumeric(); + String extendName = getNetworkModeString(network.getNetworMode(), plmnName); + pref.setTitle(plmnName + "(" + extendName + ")"); + mUPLMNListContainer.addPreference(pref); + mPreferenceMap.put(pref, network); + } + + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, + Preference preference) { + Intent intent = new Intent(this, UserPLMNEditorActivity.class); + UPLMNInfoWithEf info = this.mPreferenceMap.get(preference); + mOldInfo = info; + + intent.putExtra(UserPLMNEditorActivity.UPLMN_CODE, info.getOperatorNumeric()); + intent.putExtra(UserPLMNEditorActivity.UPLMN_PRIORITY, info.getPriority()); + intent.putExtra(UserPLMNEditorActivity.UPLMN_SERVICE, info.getNetworMode()); + intent.putExtra(UserPLMNEditorActivity.UPLMN_ADD, false); + intent.putExtra(UserPLMNEditorActivity.UPLMN_SIZE, mUPLMNList.size()); + startActivityForResult(intent, UPLMNLIST_EDIT); + return true; + } + + protected void onActivityResult(final int requestCode, + final int resultCode, final Intent intent) { + log("resultCode = " + resultCode + ", requestCode = " + requestCode); + + if (intent != null) { + UPLMNInfoWithEf newInfo = createNetworkInfofromIntent(intent); + if (resultCode == UserPLMNEditorActivity.RESULT_CODE_DELETE) { + handleSetUPLMN(handleDeleteList(mOldInfo)); + } else if (resultCode == UserPLMNEditorActivity.RESULT_CODE_EDIT) { + if (requestCode == UPLMNLIST_ADD) { + handleAddList(newInfo); + } else if (requestCode == UPLMNLIST_EDIT) { + handleSetUPLMN(handleModifiedList(newInfo, mOldInfo)); + } + } + } + } + + private UPLMNInfoWithEf createNetworkInfofromIntent(Intent intent) { + String numberName = intent.getStringExtra(UserPLMNEditorActivity.UPLMN_CODE); + int priority = intent.getIntExtra(UserPLMNEditorActivity.UPLMN_PRIORITY, 0); + int act = intent.getIntExtra(UserPLMNEditorActivity.UPLMN_SERVICE, 0); + return new UPLMNInfoWithEf(numberName, act, priority); + } + + public static byte[] stringToBcdPlmn(String str) { + if (str.length() == 5) { + str = str + "f"; + } + + byte[] trans = IccUtils.hexStringToBytes(str); + + byte[] data = new byte[3];; + data[0] = (byte) ((trans[0] >> 4) | ((trans[0] << 4) & 0xF0)); + data[1] = (byte) ((trans[1] >> 4) | ((trans[2] << 4) & 0xF0)); + data[2] = (byte) ((trans[2] & 0xF0) | (trans[1] & 0xF)); + + return data; + } + + private void handleSetUPLMN(ArrayList<UPLMNInfoWithEf> list) { + showSavingDialog(); + byte[] data = new byte[mNumRec * UPLMN_SEL_DATA_LEN]; + byte[] mccmnc = new byte[6]; + for (int i = 0; i < mNumRec; i++) { + data[i * UPLMN_SEL_DATA_LEN] = (byte) 0xFF; + data[i * UPLMN_SEL_DATA_LEN + 1] = (byte) 0xFF; + data[i * UPLMN_SEL_DATA_LEN + 2] = (byte) 0xFF; + + data[i * UPLMN_SEL_DATA_LEN + 3] = 0; + data[i * UPLMN_SEL_DATA_LEN + 4] = 0; + } + for (int i = 0; ((i < list.size()) && (i < mNumRec)); i++) { + UPLMNInfoWithEf ni = list.get(i); + String strOperNumeric = ni.getOperatorNumeric(); + if (TextUtils.isEmpty(strOperNumeric)) { + break; + } + log("strOperNumeric = " + strOperNumeric); + + System.arraycopy(stringToBcdPlmn(strOperNumeric), + 0, data, i * UPLMN_SEL_DATA_LEN, 3); + log("data[0] = " + data[i * UPLMN_SEL_DATA_LEN]); + log("data[1] = " + data[i * UPLMN_SEL_DATA_LEN +1]); + log("data[2] = " + data[i * UPLMN_SEL_DATA_LEN +2]); + + int accessTech = convertNetworkMode2AccessTech(ni.getNetworMode()); + data[i * UPLMN_SEL_DATA_LEN + 3] = (byte) (accessTech >> 8); + data[i * UPLMN_SEL_DATA_LEN + 4] = (byte) (accessTech & 0xFF); + log("accessTech = " + accessTech); + log("data[3] = " + data[i * UPLMN_SEL_DATA_LEN +3]); + log("data[4] = " + data[i * UPLMN_SEL_DATA_LEN +4]); + } + + log("update EFuplmn Start."); + writeEfToIcc(data, IccConstants.EF_PLMN_W_ACT); + } + + private void handleAddList(UPLMNInfoWithEf newInfo) { + log("handleAddList: add new network: " + newInfo); + dumpUPLMNInfo(mUPLMNList); + ArrayList<UPLMNInfoWithEf> list = new ArrayList<UPLMNInfoWithEf>(); + for (int i = 0; i < mUPLMNList.size(); i++) { + list.add(mUPLMNList.get(i)); + } + PriorityCompare pc = new PriorityCompare(); + int position = Collections.binarySearch(mUPLMNList, newInfo, pc); + if (position >= 0) + list.add(position, newInfo); + else + list.add(newInfo); + updateListPriority(list); + dumpUPLMNInfo(list); + handleSetUPLMN(list); + } + + private void dumpUPLMNInfo(List<UPLMNInfoWithEf> list) { + if (!DBG) { + return; + } + for (int i = 0; i < list.size(); i++) { + log("dumpUPLMNInfo : " + list.get(i).toString()); + } + } + + private ArrayList<UPLMNInfoWithEf> handleModifiedList( + UPLMNInfoWithEf newInfo, UPLMNInfoWithEf oldInfo) { + log("handleModifiedList: change old info: " + oldInfo.toString() + + " new info: " + newInfo.toString()); + dumpUPLMNInfo(mUPLMNList); + + PriorityCompare pc = new PriorityCompare(); + int oldposition = Collections.binarySearch(mUPLMNList, oldInfo, pc); + int newposition = Collections.binarySearch(mUPLMNList, newInfo, pc); + + ArrayList<UPLMNInfoWithEf> list = new ArrayList<UPLMNInfoWithEf>(); + for (int i = 0; i < mUPLMNList.size(); i++) { + list.add(mUPLMNList.get(i)); + } + + if (oldposition > newposition) { + list.remove(oldposition); + list.add(newposition, newInfo); + } else if (oldposition < newposition) { + list.add(newposition + 1, newInfo); + list.remove(oldposition); + } else { + list.remove(oldposition); + list.add(oldposition, newInfo); + } + + updateListPriority(list); + dumpUPLMNInfo(list); + return list; + } + + private void updateListPriority(ArrayList<UPLMNInfoWithEf> list) { + int priority = 0; + for (UPLMNInfoWithEf info : list) { + info.setPriority(priority++); + } + } + + private ArrayList<UPLMNInfoWithEf> handleDeleteList(UPLMNInfoWithEf network) { + log("handleDeleteList : " + network.toString()); + dumpUPLMNInfo(mUPLMNList); + + ArrayList<UPLMNInfoWithEf> list = new ArrayList<UPLMNInfoWithEf>(); + PriorityCompare pc = new PriorityCompare(); + int position = Collections.binarySearch(mUPLMNList, network, pc); + + for (int i = 0; i < mUPLMNList.size(); i++) { + list.add(mUPLMNList.get(i)); + } + + if (position >= 0) { + list.remove(position); + } + network.setOperatorNumeric(null); + list.add(network); + + updateListPriority(list); + dumpUPLMNInfo(list); + + return list; + } + + private class MyHandler extends Handler { + private static final int MESSAGE_GET_UPLMN_LIST = 0; + + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_GET_UPLMN_LIST: + handleGetUPLMNList(msg); + break; + default: + break; + } + } + + public void handleGetUPLMNList(Message msg) { + if (DBG) { + log("handleGetUPLMNList: done"); + } + + if (msg.arg2 == MyHandler.MESSAGE_GET_UPLMN_LIST) { + onFinished(mUPLMNListContainer, true); + } else { + onFinished(mUPLMNListContainer, false); + } + + AsyncResult ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + log("handleGetUPLMNList with exception = " + + ar.exception); + if (mUPLMNList == null) { + mUPLMNList = new ArrayList<UPLMNInfoWithEf>(); + } + } else { + refreshUPLMNListPreference((ArrayList<UPLMNInfoWithEf>) ar.result); + } + } + } + + public void handleGetEFDone(byte[] data) { + if (DBG) { + log("handleGetEFDone: done"); + } + + mNumRec = data.length / UPLMN_SEL_DATA_LEN; + log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data)); + + PlmnActRecord[] plmnActRecords = PlmnActRecord.getRecords(data); + log("PlmnActRecords=" + Arrays.toString(plmnActRecords)); + + ArrayList<UPLMNInfoWithEf> ret = new ArrayList<UPLMNInfoWithEf>(); + int i = 0; + String INVALID_PLMN_PREFIX = "FFFFF"; + for (PlmnActRecord record : plmnActRecords) { + if(!record.plmn.regionMatches( + true, 0, INVALID_PLMN_PREFIX, 0, INVALID_PLMN_PREFIX.length()) + && record.accessTechs != -1 && !TextUtils.isEmpty(record.plmn) + && record.plmn.length() >= 5){ + ret.add(new UPLMNInfoWithEf( + record.plmn, + convertAccessTech2NetworkMode(record.accessTechs), + i)); + } + i++; + } + + Message message = mHandler.obtainMessage(); + message.what = MyHandler.MESSAGE_GET_UPLMN_LIST; + if (ret == null) { + log("handleGetEFDone : NULL ret list!"); + } else { + log("handleGetEFDone : ret.size()" + ret.size()); + } + + message.obj = new AsyncResult(message.obj, (Object) ret, null); + mHandler.sendMessage(message); + } + + private int convertAccessTech2NetworkMode(int accessTechs) { + int networkMode = 0; + + if ((accessTechs & PlmnActRecord.ACCESS_TECH_EUTRAN) != 0) { + networkMode = networkMode | LTE_MASK; + } + if ((accessTechs & PlmnActRecord.ACCESS_TECH_UTRAN) != 0) { + networkMode = networkMode | UMTS_MASK; + } + if ((accessTechs & PlmnActRecord.ACCESS_TECH_GSM) != 0) { + networkMode = networkMode | GSM_MASK; + } + if ((accessTechs & PlmnActRecord.ACCESS_TECH_GSM_COMPACT) != 0) { + networkMode = networkMode | GSM_COMPACT_MASK; + } + + return networkMode; + } + + private int convertNetworkMode2AccessTech(int networkMode) { + int accessTechs = 0; + + if ((networkMode & LTE_MASK) != 0) { + accessTechs = accessTechs | PlmnActRecord.ACCESS_TECH_EUTRAN; + } + if ((networkMode & UMTS_MASK) != 0) { + accessTechs = accessTechs | PlmnActRecord.ACCESS_TECH_UTRAN; + } + if ((networkMode & GSM_MASK) != 0) { + accessTechs = accessTechs | PlmnActRecord.ACCESS_TECH_GSM; + } + if ((networkMode & GSM_COMPACT_MASK) != 0) { + accessTechs = accessTechs | PlmnActRecord.ACCESS_TECH_GSM_COMPACT; + } + + return accessTechs; + } + + private String getNetworkModeString(int EFNWMode, String plmn) { + Log.d(LOG_TAG, "plmn = " + plmn); + int index = UserPLMNEditorActivity.convertEFMode2Ap(EFNWMode); + String[] CuPlmnArray = getResources().getStringArray(R.array.uplmn_cu_mcc_mnc_values); + for (String CuPlmn : CuPlmnArray) { + if (plmn.equals(CuPlmn)) { + return getResources().getStringArray + (R.array.uplmn_prefer_network_mode_w_choices)[index]; + } + } + return getResources().getStringArray(R.array.uplmn_prefer_network_mode_td_choices)[index]; + } + + private void setScreenEnabled() { + getPreferenceScreen().setEnabled(!mAirplaneModeOn); + invalidateOptionsMenu(); + } + + private static void log(String msg) { + Log.d(LOG_TAG, msg); + } +} diff --git a/src/com/android/settings/network/telephony/UserPLMNPreferenceController.java b/src/com/android/settings/network/telephony/UserPLMNPreferenceController.java new file mode 100755 index 0000000000..76008cdbd1 --- /dev/null +++ b/src/com/android/settings/network/telephony/UserPLMNPreferenceController.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.settings.network.telephony; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + +import com.android.settings.network.telephony.UserPLMNListActivity; + +/** + * Preference controller for "User PLMN Settings" + */ +public class UserPLMNPreferenceController extends TelephonyBasePreferenceController { + + @VisibleForTesting + CarrierConfigManager mCarrierConfigManager; + + public UserPLMNPreferenceController(Context context, String key) { + super(context, key); + mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); + } + + public void init(int subId) { + mSubId = subId; + } + + @Override + public int getAvailabilityStatus(int subId) { + return SubscriptionManager.isValidSubscriptionId(subId) + ? AVAILABLE + : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (getPreferenceKey().equals(preference.getKey())) { + final Intent intent = new Intent(mContext, UserPLMNListActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mSubId); + mContext.startActivity(intent); + return true; + } + + return false; + } +} diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java index 2d7ba38a80..fbb0de9b83 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java @@ -36,6 +36,7 @@ import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.ims.ImsConfig; import com.android.settings.R; import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -155,6 +156,9 @@ public class WifiCallingPreferenceController extends TelephonyBasePreferenceCont case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; break; + case ImsConfig.WfcModeFeatureValueConstants.IMS_PREFERRED: + resId = com.android.internal.R.string.wfc_mode_ims_preferred_summary; + break; default: break; } diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java index 028c4e77a7..a02d833e60 100644 --- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java @@ -43,6 +43,8 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.network.AllowedNetworkTypesListener; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.telephony.Enhanced4gBasePreferenceController; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.TelephonyTogglePreferenceController; import com.android.settingslib.utils.ThreadUtils; @@ -56,7 +58,9 @@ import java.util.concurrent.TimeUnit; * Preference controller for "Auto Select Network" */ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceController - implements LifecycleObserver{ + implements LifecycleObserver, + Enhanced4gBasePreferenceController.On4gLteUpdateListener, + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { private static final long MINIMUM_DIALOG_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1); private final Handler mUiHandler; @@ -65,6 +69,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon private TelephonyManager mTelephonyManager; private boolean mOnlyAutoSelectInHome; private List<OnNetworkSelectModeListener> mListeners; + private SubscriptionsChangeListener mSubscriptionsListener; @VisibleForTesting ProgressDialog mProgressDialog; @VisibleForTesting @@ -80,6 +85,12 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon new HandlerExecutor(mUiHandler)); mAllowedNetworkTypesListener.setAllowedNetworkTypesListener( () -> updatePreference()); + mSubscriptionsListener = new SubscriptionsChangeListener(context, this); + } + + @Override + public void on4gLteUpdated() { + updateState(mSwitchPreference); } private void updatePreference() { @@ -94,11 +105,13 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon @OnLifecycleEvent(ON_START) public void onStart() { mAllowedNetworkTypesListener.register(mContext, mSubId); + mSubscriptionsListener.start(); } @OnLifecycleEvent(ON_STOP) public void onStop() { mAllowedNetworkTypesListener.unregister(mContext, mSubId); + mSubscriptionsListener.stop(); } @Override @@ -124,8 +137,12 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon @Override public void updateState(Preference preference) { super.updateState(preference); - preference.setSummary(null); + final int phoneType = mTelephonyManager.getPhoneType(); + if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) { + preference.setEnabled(false); + return; + } final ServiceState serviceState = mTelephonyManager.getServiceState(); if (serviceState == null) { preference.setEnabled(false); @@ -230,6 +247,14 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon } } + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + } + + @Override + public void onSubscriptionsChanged() { + updateState(mSwitchPreference); + } /** * Callback when network select mode is changed * diff --git a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java index 54f5ce15d7..fa488744e8 100644 --- a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java @@ -34,6 +34,8 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.network.AllowedNetworkTypesListener; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.telephony.Enhanced4gBasePreferenceController; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.TelephonyBasePreferenceController; @@ -42,12 +44,16 @@ import com.android.settings.network.telephony.TelephonyBasePreferenceController; */ public class OpenNetworkSelectPagePreferenceController extends TelephonyBasePreferenceController implements - AutoSelectPreferenceController.OnNetworkSelectModeListener, LifecycleObserver { + AutoSelectPreferenceController.OnNetworkSelectModeListener, + Enhanced4gBasePreferenceController.On4gLteUpdateListener, + LifecycleObserver, + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { private TelephonyManager mTelephonyManager; private Preference mPreference; private PreferenceScreen mPreferenceScreen; private AllowedNetworkTypesListener mAllowedNetworkTypesListener; + private SubscriptionsChangeListener mSubscriptionsListener; public OpenNetworkSelectPagePreferenceController(Context context, String key) { super(context, key); @@ -57,9 +63,15 @@ public class OpenNetworkSelectPagePreferenceController extends context.getMainExecutor()); mAllowedNetworkTypesListener.setAllowedNetworkTypesListener( () -> updatePreference()); + mSubscriptionsListener = new SubscriptionsChangeListener(context, this); } + @Override + public void on4gLteUpdated() { + updateState(mPreference); + } + private void updatePreference() { if (mPreferenceScreen != null) { displayPreference(mPreferenceScreen); @@ -79,11 +91,13 @@ public class OpenNetworkSelectPagePreferenceController extends @OnLifecycleEvent(ON_START) public void onStart() { mAllowedNetworkTypesListener.register(mContext, mSubId); + mSubscriptionsListener.start(); } @OnLifecycleEvent(ON_STOP) public void onStop() { mAllowedNetworkTypesListener.unregister(mContext, mSubId); + mSubscriptionsListener.stop(); } @Override @@ -96,8 +110,13 @@ public class OpenNetworkSelectPagePreferenceController extends @Override public void updateState(Preference preference) { super.updateState(preference); + final int phoneType = mTelephonyManager.getPhoneType(); + if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) { + preference.setEnabled(false); + } else { preference.setEnabled(mTelephonyManager.getNetworkSelectionMode() != TelephonyManager.NETWORK_SELECTION_MODE_AUTO); + } Intent intent = new Intent(); intent.setClassName("com.android.settings", @@ -128,4 +147,13 @@ public class OpenNetworkSelectPagePreferenceController extends public void onNetworkSelectModeChanged() { updateState(mPreference); } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + } + + @Override + public void onSubscriptionsChanged() { + updateState(mPreference); + } } diff --git a/src/com/android/settings/notification/CallConnectedTonePreferenceController.java b/src/com/android/settings/notification/CallConnectedTonePreferenceController.java new file mode 100644 index 0000000000..9498485990 --- /dev/null +++ b/src/com/android/settings/notification/CallConnectedTonePreferenceController.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 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.notification; + +import static com.android.settings.notification.SettingPref.TYPE_SYSTEM; + +import android.content.Context; +import android.provider.Settings.System; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; +import com.android.settingslib.core.lifecycle.Lifecycle; + +public class CallConnectedTonePreferenceController extends SettingPrefController { + + private static final String KEY_CALL_CONNECTED_TONES = "call_connected_tones"; + + public CallConnectedTonePreferenceController(Context context, SettingsPreferenceFragment parent, + Lifecycle lifecycle) { + super(context, parent, lifecycle); + + int defaultOn = mContext.getResources().getInteger(R.integer. + config_default_tone_after_connected); + mPreference = new SettingPref( + TYPE_SYSTEM, KEY_CALL_CONNECTED_TONES, System.CALL_CONNECTED_TONE_ENABLED, + defaultOn) { + @Override + public boolean isApplicable(Context context) { + return context.getResources().getBoolean(R.bool.config_show_connect_tone_ui); + } + }; + } + +} diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java index 85623b8a9f..94cd529741 100644 --- a/src/com/android/settings/notification/SoundSettings.java +++ b/src/com/android/settings/notification/SoundSettings.java @@ -248,6 +248,8 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult // === Other Sound Settings === final DialPadTonePreferenceController dialPadTonePreferenceController = new DialPadTonePreferenceController(context, fragment, lifecycle); + final CallConnectedTonePreferenceController callConnectedTonePreferenceController = + new CallConnectedTonePreferenceController(context, fragment, lifecycle); final ScreenLockSoundPreferenceController screenLockSoundPreferenceController = new ScreenLockSoundPreferenceController(context, fragment, lifecycle); final ChargingSoundPreferenceController chargingSoundPreferenceController = @@ -266,6 +268,7 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult new EmergencyTonePreferenceController(context, fragment, lifecycle); controllers.add(dialPadTonePreferenceController); + controllers.add(callConnectedTonePreferenceController); controllers.add(screenLockSoundPreferenceController); controllers.add(chargingSoundPreferenceController); controllers.add(dockingSoundPreferenceController); @@ -277,6 +280,7 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult controllers.add(new PreferenceCategoryController(context, "other_sounds_and_vibrations_category").setChildren( Arrays.asList(dialPadTonePreferenceController, + callConnectedTonePreferenceController, screenLockSoundPreferenceController, chargingSoundPreferenceController, dockingSoundPreferenceController, diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index a1826ba0cb..87ea9c2e62 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -215,6 +215,7 @@ public class ChooseLockGeneric extends SettingsActivity { mLockPatternUtils = new LockPatternUtils(activity); mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction) || ACTION_SET_NEW_PASSWORD.equals(chooseLockAction); + mLockPatternUtils.sanitizePassword(); // Defaults to needing to confirm credentials final boolean confirmCredentials = intent @@ -901,6 +902,7 @@ public class ChooseLockGeneric extends SettingsActivity { @Override public void onDestroy() { super.onDestroy(); + mLockPatternUtils.sanitizePassword(); if (mUserPassword != null) { mUserPassword.zeroize(); } diff --git a/src/com/android/settings/security/SimLockPreferenceController.java b/src/com/android/settings/security/SimLockPreferenceController.java index 3b8588852e..28f6924337 100644 --- a/src/com/android/settings/security/SimLockPreferenceController.java +++ b/src/com/android/settings/security/SimLockPreferenceController.java @@ -112,8 +112,10 @@ public class SimLockPreferenceController extends BasePreferenceController { .createForSubscriptionId(subInfo.getSubscriptionId()); final PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId( subInfo.getSubscriptionId()); + final int simState = mTelephonyManager.getSimState(subInfo.getSimSlotIndex()); if (telephonyManager.hasIccCard() && bundle != null - && !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL)) { + && !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL) + && (simState != TelephonyManager.SIM_STATE_NOT_READY)) { // one or more sims show sim lock setting UI. return false; } diff --git a/src/com/android/settings/widget/GroupOptionsPreference.java b/src/com/android/settings/widget/GroupOptionsPreference.java new file mode 100644 index 0000000000..eddbe5f85f --- /dev/null +++ b/src/com/android/settings/widget/GroupOptionsPreference.java @@ -0,0 +1,414 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settings.widget; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.StringRes; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; + +/** A preference which provide Group action buttons (connect, disconnect, + * refresh, cacnelRefresh, forget + **/ + +public class GroupOptionsPreference extends Preference { + + private static final String TAG = "GroupOptionsPreference"; + private final ButtonInfo mBtnAddSrcGroup = new ButtonInfo(); + private final ButtonInfo mBtnConnect = new ButtonInfo(); + private final ButtonInfo mBtnForget = new ButtonInfo(); + private final ButtonInfo mBtnDisconnect = new ButtonInfo(); + private final ButtonInfo mBtnRefresh = new ButtonInfo(); + private final ButtonInfo mBtnCancelRefresh = new ButtonInfo(); + private final TextViewInfo mTvGroupId = new TextViewInfo(); + private final TextViewInfo mTvStatus = new TextViewInfo(); + private final ProgressInfo mProgressScan = new ProgressInfo(); + + public GroupOptionsPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public GroupOptionsPreference(Context context, AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public GroupOptionsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + setLayoutResource(R.layout.bluetooth_group_options); + setSelectable(false); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.setDividerAllowedAbove(true); + holder.setDividerAllowedBelow(true); + mBtnAddSrcGroup.mButton = (Button) holder.findViewById(R.id.id_btn_group_add_source); + mBtnConnect.mButton = (Button) holder.findViewById(R.id.id_btn_connect); + mBtnForget.mButton = (Button) holder.findViewById(R.id.id_btn_forget); + mBtnDisconnect.mButton = (Button) holder.findViewById(R.id.id_btn_disconnect); + mBtnRefresh.mButton = (Button) holder.findViewById(R.id.id_btn_refresh); + mBtnCancelRefresh.mButton = (Button) holder.findViewById(R.id.id_btn_refresh_cancel); + mTvGroupId.mTextView = (TextView)holder.findViewById(R.id.id_tv_groupid); + mTvStatus.mTextView =(TextView)holder.findViewById(R.id.id_tv_status); + mProgressScan.mProgress = (ProgressBar)holder.findViewById(R.id.id_progress_group_scan); + mBtnAddSrcGroup.setUpButton(); + mBtnConnect.setUpButton(); + mBtnForget.setUpButton(); + mBtnDisconnect.setUpButton(); + mBtnRefresh.setUpButton(); + mBtnCancelRefresh.setUpButton(); + mTvGroupId.setUpTextView(); + mTvStatus.setUpTextView(); + mProgressScan.setUpProgress(); + } + + public GroupOptionsPreference setAddSourceGroupButtonVisible(boolean isVisible) { + if (isVisible != mBtnAddSrcGroup.mIsVisible) { + mBtnAddSrcGroup.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setAddSourceGroupButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnAddSrcGroup.mText)) { + mBtnAddSrcGroup.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setAddSourceGroupButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnAddSrcGroup.mIsEnabled) { + mBtnAddSrcGroup.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setAddSourceGroupButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnAddSrcGroup.mListener) { + mBtnAddSrcGroup.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setConnectButtonVisible(boolean isVisible) { + if (isVisible != mBtnConnect.mIsVisible) { + mBtnConnect.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setConnectButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnConnect.mText)) { + mBtnConnect.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setConnectButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnConnect.mIsEnabled) { + mBtnConnect.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setConnectButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnConnect.mListener) { + mBtnConnect.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setForgettButtonVisible(boolean isVisible) { + if (isVisible != mBtnForget.mIsVisible) { + mBtnForget.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setForgetButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnForget.mText)) { + mBtnForget.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setForgetButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnForget.mIsEnabled) { + mBtnForget.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setForgetButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnForget.mListener) { + mBtnForget.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setDisconnectButtonVisible(boolean isVisible) { + if (isVisible != mBtnDisconnect.mIsVisible) { + mBtnDisconnect.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setDisconnectButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnDisconnect.mText)) { + mBtnDisconnect.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setDisconnectButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnDisconnect.mIsEnabled) { + mBtnDisconnect.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setDisconnectButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnDisconnect.mListener) { + mBtnDisconnect.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setRefreshButtonVisible(boolean isVisible) { + if (isVisible != mBtnRefresh.mIsVisible) { + mBtnRefresh.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setRefreshButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnRefresh.mText)) { + mBtnRefresh.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setRefreshButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnRefresh.mIsEnabled) { + mBtnRefresh.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setRefreshButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnRefresh.mListener) { + mBtnRefresh.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setCancelRefreshButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mBtnCancelRefresh.mText)) { + mBtnCancelRefresh.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setCancelRefreshButtonVisible(boolean isVisible) { + if (isVisible != mBtnCancelRefresh.mIsVisible) { + mBtnCancelRefresh.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setCancelRefreshButtonEnabled(boolean isEnabled) { + if (isEnabled != mBtnCancelRefresh.mIsEnabled) { + mBtnCancelRefresh.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setCancelRefreshButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mBtnCancelRefresh.mListener) { + mBtnCancelRefresh.mListener = listener; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setTvSetIdVisible(boolean isVisible) { + if (isVisible != mTvGroupId.mIsVisible) { + mTvGroupId.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setTextViewText(String newText) { + if (!TextUtils.equals(newText, mTvGroupId.mText)) { + mTvGroupId.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setTvStatusVisible(boolean isVisible) { + if (isVisible != mTvStatus.mIsVisible) { + mTvStatus.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setTexStatusText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mTvStatus.mText)) { + mTvStatus.mText = newText; + notifyChanged(); + } + return this; + } + + public GroupOptionsPreference setProgressScanVisible(boolean isVisible) { + if (isVisible != mProgressScan.mIsVisible) { + mProgressScan.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + static class ButtonInfo { + private Button mButton; + private CharSequence mText; + private View.OnClickListener mListener; + private boolean mIsEnabled = true; + private boolean mIsVisible = true; + + void setUpButton() { + mButton.setText(mText); + mButton.setOnClickListener(mListener); + mButton.setEnabled(mIsEnabled); + mButton.setTypeface(null, Typeface.BOLD); + if (shouldBeVisible()) { + mButton.setVisibility(View.VISIBLE); + } else { + mButton.setVisibility(View.GONE); + } + } + + private boolean shouldBeVisible() { + return mIsVisible && (!TextUtils.isEmpty(mText)); + } + } + + static class TextViewInfo { + private TextView mTextView; + private CharSequence mText; + private boolean mIsVisible = true; + + void setUpTextView() { + mTextView.setText(mText); + mTextView.setTypeface(null, Typeface.BOLD); + if (shouldBeVisible()) { + mTextView.setVisibility(View.VISIBLE); + } else { + mTextView.setVisibility(View.INVISIBLE); + } + } + + private boolean shouldBeVisible() { + return mIsVisible && (!TextUtils.isEmpty(mText)); + } + } + + static class ProgressInfo { + private ProgressBar mProgress; + private boolean mIsVisible = true; + void setUpProgress() { + if (shouldBeVisible()) { + mProgress.setVisibility(View.VISIBLE); + } else { + mProgress.setVisibility(View.INVISIBLE); + } + } + + private boolean shouldBeVisible() { + return mIsVisible; + } + } +} diff --git a/src/com/android/settings/widget/GroupPreferenceCategory.java b/src/com/android/settings/widget/GroupPreferenceCategory.java new file mode 100644 index 0000000000..06c7858bf9 --- /dev/null +++ b/src/com/android/settings/widget/GroupPreferenceCategory.java @@ -0,0 +1,66 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +package com.android.settings.widget; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.preference.PreferenceCategory; + +/** A PreferenceCategory which holds setid + **/ +public class GroupPreferenceCategory extends PreferenceCategory { + + private int mGroupId = -1; + + public GroupPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public GroupPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public GroupPreferenceCategory(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GroupPreferenceCategory(Context context) { + super(context); + } + + public int getGroupId() { + return mGroupId; + } + + public void setGroupId(int groupId) { + mGroupId = groupId; + } +} diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java index 127c882340..e9206bc054 100644 --- a/src/com/android/settings/wifi/WifiConfigController2.java +++ b/src/com/android/settings/wifi/WifiConfigController2.java @@ -203,6 +203,7 @@ public class WifiConfigController2 implements TextWatcher, private TextView mProxyExclusionListView; private TextView mProxyPacView; private CheckBox mSharedCheckBox; + private CheckBox mShareThisWifiCheckBox; private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED; private ProxySettings mProxySettings = ProxySettings.UNASSIGNED; @@ -292,11 +293,19 @@ public class WifiConfigController2 implements TextWatcher, ? View.GONE : View.VISIBLE); mSecurityInPosition = new Integer[WifiEntry.NUM_SECURITY_TYPES]; + mShareThisWifiCheckBox = (CheckBox) mView.findViewById(R.id.share_this_wifi); if (mWifiEntry == null) { // new network configureSecuritySpinner(); mConfigUi.setSubmitButton(res.getString(R.string.wifi_save)); } else { + if (!mWifiManager.isWifiCoverageExtendFeatureEnabled() + || (mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE + && mWifiEntry.getSecurity() != WifiEntry.SECURITY_PSK)) { + mShareThisWifiCheckBox.setChecked(false); + mShareThisWifiCheckBox.setVisibility(View.GONE); + } + mConfigUi.setTitle(mWifiEntry.getTitle()); ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); @@ -304,6 +313,7 @@ public class WifiConfigController2 implements TextWatcher, boolean showAdvancedFields = false; if (mWifiEntry.isSaved()) { WifiConfiguration config = mWifiEntry.getWifiConfiguration(); + mShareThisWifiCheckBox.setChecked(config.shareThisAp); mMeteredSettingsSpinner.setSelection(config.meteredOverride); mHiddenSettingsSpinner.setSelection(config.hiddenSSID ? HIDDEN_NETWORK @@ -597,6 +607,7 @@ public class WifiConfigController2 implements TextWatcher, } config.shared = mSharedCheckBox.isChecked(); + config.shareThisAp = mShareThisWifiCheckBox.isChecked(); switch (mWifiEntrySecurity) { case WifiEntry.SECURITY_NONE: @@ -697,8 +708,10 @@ public class WifiConfigController2 implements TextWatcher, if (config.enterpriseConfig.isAuthenticationSimBased() && mActiveSubscriptionInfos.size() > 0) { - config.carrierId = mActiveSubscriptionInfos - .get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId(); + SubscriptionInfo subInfo = mActiveSubscriptionInfos + .get(mEapSimSpinner.getSelectedItemPosition()); + config.carrierId = subInfo.getCarrierId(); + config.subscriptionId = subInfo.getSubscriptionId(); } String caCert = (String) mEapCaCertSpinner.getSelectedItem(); @@ -1665,6 +1678,16 @@ public class WifiConfigController2 implements TextWatcher, if (parent == mSecuritySpinner) { // Convert menu position to actual Wi-Fi security type mWifiEntrySecurity = mSecurityInPosition[position]; + + if (!mWifiManager.isWifiCoverageExtendFeatureEnabled() + || (mWifiEntrySecurity != WifiEntry.SECURITY_NONE + && mWifiEntrySecurity != WifiEntry.SECURITY_PSK)) { + mShareThisWifiCheckBox.setChecked(false); + mShareThisWifiCheckBox.setVisibility(View.GONE); + } else { + mShareThisWifiCheckBox.setVisibility(View.VISIBLE); + } + showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true); if (WifiDppUtils.isSupportEnrolleeQrCodeScanner(mContext, mWifiEntrySecurity)) { diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 4c7be5871d..44df512ada 100644..100755 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -190,6 +190,8 @@ public class WifiSettings extends RestrictedSettingsFragment // Worker thread used for WifiPickerTracker work private HandlerThread mWorkerThread; + private Handler mMainHandler; + private Handler mWorkerHandler; @VisibleForTesting WifiPickerTracker mWifiPickerTracker; @@ -291,11 +293,13 @@ public class WifiSettings extends RestrictedSettingsFragment return SystemClock.elapsedRealtime(); } }; + + mMainHandler = new Handler(Looper.getMainLooper()); + mWorkerHandler = mWorkerThread.getThreadHandler(); mWifiPickerTracker = FeatureFactory.getFactory(context) .getWifiTrackerLibProvider() .createWifiPickerTracker(getSettingsLifecycle(), context, - new Handler(Looper.getMainLooper()), - mWorkerThread.getThreadHandler(), + mMainHandler, mWorkerHandler, elapsedRealtimeClock, MAX_SCAN_AGE_MILLIS, SCAN_INTERVAL_MILLIS, @@ -367,6 +371,10 @@ public class WifiSettings extends RestrictedSettingsFragment if (mWifiEnabler != null) { mWifiEnabler.teardownSwitchController(); } + + // remove all msg and callback in main handler and worker handler + mMainHandler.removeCallbacksAndMessages(null); + mWorkerHandler.removeCallbacksAndMessages(null); mWorkerThread.quit(); super.onDestroyView(); @@ -377,6 +385,7 @@ public class WifiSettings extends RestrictedSettingsFragment super.onStart(); mWifiEnabler = createWifiEnabler(); + mWifiManager.allowConnectOnPartialScanResults(true); if (mIsRestricted) { restrictUi(); @@ -431,6 +440,7 @@ public class WifiSettings extends RestrictedSettingsFragment public void onStop() { getView().removeCallbacks(mUpdateWifiEntryPreferencesRunnable); getView().removeCallbacks(mHideProgressBarRunnable); + mWifiManager.allowConnectOnPartialScanResults(false); mIsWifiEntryListStale = true; super.onStop(); } @@ -651,7 +661,7 @@ public class WifiSettings extends RestrictedSettingsFragment /** Called when the state of Wifi has changed. */ @Override public void onWifiStateChanged() { - if (mIsRestricted) { + if (mIsRestricted || isFinishingOrDestroyed()) { return; } final int wifiState = mWifiPickerTracker.getWifiState(); @@ -689,6 +699,12 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public void onWifiEntriesChanged() { + // do nothing if UI is finishing and destoryed status + if (isFinishingOrDestroyed()) { + Log.d(TAG, "onWifiEntriesChanged: isFinishingOrDestroyed true"); + return; + } + if (mIsWifiEntryListStale) { mIsWifiEntryListStale = false; updateWifiEntryPreferences(); @@ -833,7 +849,10 @@ public class WifiSettings extends RestrictedSettingsFragment mWifiEntryPreferenceCategory.addPreference(pref); } else { // Continuing showing progress bar for an additional delay to overlap with animation - getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */); + final View view = getView(); + if (null != view) { + view.postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */); + } } mAddWifiNetworkPreference.setOrder(index++); diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 19664be409..5b688ff473 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -345,6 +345,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment final CarrierConfigManager configManager = (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE); boolean isWifiOnlySupported = true; + boolean isImsPreferredSupported = false; if (configManager != null) { final PersistableBundle b = configManager.getConfigForSubId(mSubId); @@ -358,6 +359,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment false); isWifiOnlySupported = b.getBoolean( CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); + isImsPreferredSupported = b.getBoolean( + CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_IMS_PREFERRED_BOOL, false); } } @@ -368,37 +371,72 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment mButtonWfcRoamingMode.setDialogTitle( res.getString(R.string.wifi_calling_roaming_mode_dialog_title)); + Log.d(TAG, "isWifiOnlySupported = " + isWifiOnlySupported + " isImsPreferredSupported = " + + isImsPreferredSupported); + if (isWifiOnlySupported) { - // Set string resources WITH option wifi only in mButtonWfcMode. - mButtonWfcMode.setEntries( - res.getStringArray(R.array.wifi_calling_mode_choices)); - mButtonWfcMode.setEntryValues(res.getStringArray(R.array.wifi_calling_mode_values)); - mButtonWfcMode.setEntrySummaries( - res.getStringArray(R.array.wifi_calling_mode_summaries)); - - // Set string resources WITH option wifi only in mButtonWfcRoamingMode. - mButtonWfcRoamingMode.setEntries( - res.getStringArray(R.array.wifi_calling_mode_choices_v2)); - mButtonWfcRoamingMode.setEntryValues( - res.getStringArray(R.array.wifi_calling_mode_values)); - mButtonWfcRoamingMode.setEntrySummaries( - res.getStringArray(R.array.wifi_calling_mode_summaries)); + if (isImsPreferredSupported) { + mButtonWfcMode.setEntries(res.getStringArray( + R.array.wifi_calling_mode_choices_with_ims_preferred)); + mButtonWfcMode.setEntryValues(res.getStringArray( + R.array.wifi_calling_mode_values_with_ims_preferred)); + mButtonWfcMode.setEntrySummaries(res.getStringArray( + R.array.wifi_calling_mode_summaries_with_ims_preferred)); + + mButtonWfcRoamingMode.setEntries(res.getStringArray( + R.array.wifi_calling_mode_choices_v2_with_ims_preferred)); + mButtonWfcRoamingMode.setEntryValues(res.getStringArray( + R.array.wifi_calling_mode_values_with_ims_preferred)); + mButtonWfcRoamingMode.setEntrySummaries(res.getStringArray( + R.array.wifi_calling_mode_summaries_with_ims_preferred)); + } else { + // Set string resources WITH option wifi only in mButtonWfcMode. + mButtonWfcMode.setEntries( + res.getStringArray(R.array.wifi_calling_mode_choices)); + mButtonWfcMode.setEntryValues(res.getStringArray(R.array.wifi_calling_mode_values)); + mButtonWfcMode.setEntrySummaries( + res.getStringArray(R.array.wifi_calling_mode_summaries)); + + // Set string resources WITH option wifi only in mButtonWfcRoamingMode. + mButtonWfcRoamingMode.setEntries( + res.getStringArray(R.array.wifi_calling_mode_choices_v2)); + mButtonWfcRoamingMode.setEntryValues( + res.getStringArray(R.array.wifi_calling_mode_values)); + mButtonWfcRoamingMode.setEntrySummaries( + res.getStringArray(R.array.wifi_calling_mode_summaries)); + } } else { - // Set string resources WITHOUT option wifi only in mButtonWfcMode. - mButtonWfcMode.setEntries( - res.getStringArray(R.array.wifi_calling_mode_choices_without_wifi_only)); - mButtonWfcMode.setEntryValues( - res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); - mButtonWfcMode.setEntrySummaries( - res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); - - // Set string resources WITHOUT option wifi only in mButtonWfcRoamingMode. - mButtonWfcRoamingMode.setEntries( - res.getStringArray(R.array.wifi_calling_mode_choices_v2_without_wifi_only)); - mButtonWfcRoamingMode.setEntryValues( - res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); - mButtonWfcRoamingMode.setEntrySummaries( - res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); + if (isImsPreferredSupported) { + mButtonWfcMode.setEntries(res.getStringArray( + R.array.wifi_calling_mode_choices_without_wifi_only_with_ims_preferred)); + mButtonWfcMode.setEntryValues(res.getStringArray( + R.array.wifi_calling_mode_values_without_wifi_only_with_ims_preferred)); + mButtonWfcMode.setEntrySummaries(res.getStringArray( + R.array.wifi_calling_mode_summaries_without_wifi_only_with_ims_preferred)); + + mButtonWfcRoamingMode.setEntries(res.getStringArray( + R.array.wifi_calling_mode_choices_v2_without_wifi_only_with_ims_preferred)); + mButtonWfcRoamingMode.setEntryValues(res.getStringArray( + R.array.wifi_calling_mode_values_without_wifi_only_with_ims_preferred)); + mButtonWfcRoamingMode.setEntrySummaries(res.getStringArray( + R.array.wifi_calling_mode_summaries_without_wifi_only_with_ims_preferred)); + } else { + // Set string resources WITHOUT option wifi only in mButtonWfcMode. + mButtonWfcMode.setEntries( + res.getStringArray(R.array.wifi_calling_mode_choices_without_wifi_only)); + mButtonWfcMode.setEntryValues( + res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); + mButtonWfcMode.setEntrySummaries( + res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); + + // Set string resources WITHOUT option wifi only in mButtonWfcRoamingMode. + mButtonWfcRoamingMode.setEntries( + res.getStringArray(R.array.wifi_calling_mode_choices_v2_without_wifi_only)); + mButtonWfcRoamingMode.setEntryValues( + res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only)); + mButtonWfcRoamingMode.setEntrySummaries( + res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only)); + } } // NOTE: Buttons will be enabled/disabled in mTelephonyCallback @@ -644,6 +682,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; break; + case ImsConfig.WfcModeFeatureValueConstants.IMS_PREFERRED: + resId = com.android.internal.R.string.wfc_mode_ims_preferred_summary; + break; default: Log.e(TAG, "Unexpected WFC mode value: " + wfcMode); } diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java index 39a5431e63..b2ebb23229 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java +++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java @@ -439,7 +439,8 @@ public class WifiDppUtils { return securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION || securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK - || securityType == SoftApConfiguration.SECURITY_TYPE_OPEN; + || securityType == SoftApConfiguration.SECURITY_TYPE_OPEN + || securityType == SoftApConfiguration.SECURITY_TYPE_OWE; } private static boolean isSupportWifiDpp(Context context, int wifiEntrySecurity) { diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java index a926360bd9..def8bb858b 100644 --- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java +++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java @@ -47,6 +47,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.EditText; import android.widget.Toast; @@ -199,7 +200,10 @@ public class WifiP2pSettings extends DashboardFragment } @Override - public void onActivityCreated(Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View root = super.onCreateView(inflater, container, savedInstanceState); + final Activity activity = getActivity(); if (mWifiP2pManager == null) { mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); @@ -332,8 +336,7 @@ public class WifiP2pSettings extends DashboardFragment } } }; - - super.onActivityCreated(savedInstanceState); + return root; } @Override diff --git a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java index 8a4be10598..1da3e72bf8 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java @@ -30,48 +30,79 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.FeatureFlags; +import java.util.ArrayList; +import java.util.Arrays; public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferenceController { private static final String TAG = "WifiTetherApBandPref"; private static final String PREF_KEY = "wifi_tether_network_ap_band"; + // Predefined Band 5Ghz / 6Ghz combinations. + // 1- 5Ghz/6Ghz prefer (default): prefers 5Ghz/6Ghz, but supports 2Ghz also. + // 2- 5Ghz/6Ghz only: strict 5Ghz/6Ghz band. + private static final int BAND_5GHZ = SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_2GHZ; + private static final int BAND_6GHZ = SoftApConfiguration.BAND_6GHZ | SoftApConfiguration.BAND_2GHZ; + private String[] mBandEntries; private String[] mBandSummaries; private int mBandIndex; + private final Context mContext; + private boolean m5GHzSupported; + private boolean m6GHzSupported; + private String mCountryCode; + + // Dual Band (2G + 5G) + public static final int BAND_BOTH_2G_5G = 1 << 4; public WifiTetherApBandPreferenceController(Context context, OnTetherConfigUpdateListener listener) { super(context, listener); + mContext = context; + + syncBandSupportAndCountryCode(); updatePreferenceEntries(); } @Override public void updateDisplay() { final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + syncBandSupportAndCountryCode(); if (config == null) { mBandIndex = SoftApConfiguration.BAND_2GHZ; Log.d(TAG, "Updating band index to BAND_2GHZ because no config"); - } else if (is5GhzBandSupported()) { - mBandIndex = validateSelection(config.getBand()); + } else if (is5GhzBandSupported() || is6GhzBandSupported()) { + if (config.getBands().length == 2) { + if (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OWE) { + mWifiManager.setSoftApConfiguration( + new SoftApConfiguration.Builder(config) + .setBand(SoftApConfiguration.BAND_2GHZ) + .build()); + mBandIndex = SoftApConfiguration.BAND_2GHZ; + Log.d(TAG, "Dual band not supported with OWE, updating band index to 2GHz"); + } else { + mBandIndex = BAND_BOTH_2G_5G; + } + } else + mBandIndex = validateSelection(config.getBand()); Log.d(TAG, "Updating band index to " + mBandIndex); } else { mWifiManager.setSoftApConfiguration( new SoftApConfiguration.Builder(config).setBand(SoftApConfiguration.BAND_2GHZ) .build()); mBandIndex = SoftApConfiguration.BAND_2GHZ; - Log.d(TAG, "5Ghz not supported, updating band index to 2GHz"); + Log.d(TAG, "5Ghz/6Ghz not supported, updating band index to 2GHz"); } ListPreference preference = (ListPreference) mPreference; preference.setEntries(mBandSummaries); preference.setEntryValues(mBandEntries); - if (!is5GhzBandSupported()) { + if (!is5GhzBandSupported() && !is6GhzBandSupported()) { preference.setEnabled(false); preference.setSummary(R.string.wifi_ap_choose_2G); } else { - preference.setValue(Integer.toString(config.getBand())); + preference.setValue(Integer.toString(mBandIndex)); preference.setSummary(getConfigSummary()); } } @@ -80,8 +111,11 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen switch (mBandIndex) { case SoftApConfiguration.BAND_2GHZ: return mBandSummaries[0]; - case SoftApConfiguration.BAND_5GHZ: - return mBandSummaries[1]; + case BAND_5GHZ: + case BAND_6GHZ: + case BAND_BOTH_2G_5G: + final ListPreference preference = (ListPreference) mPreference; + return mBandSummaries[preference.findIndexOfValue(String.valueOf(mBandIndex))]; default: return mContext.getString(R.string.wifi_ap_prefer_5G); } @@ -95,6 +129,7 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen @Override public boolean onPreferenceChange(Preference preference, Object newValue) { + syncBandSupportAndCountryCode(); mBandIndex = validateSelection(Integer.parseInt((String) newValue)); Log.d(TAG, "Band preference changed, updating band index to " + mBandIndex); preference.setSummary(getConfigSummary()); @@ -106,11 +141,17 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen // unsupported states: // 1: BAND_5GHZ only - include 2GHZ since some of countries doesn't support 5G hotspot // 2: no 5 GHZ support means we can't have BAND_5GHZ - default to 2GHZ - if (SoftApConfiguration.BAND_5GHZ == band) { + // 3: no 6 GHZ support means we can't have AP_BAND_6GHZ - default to 2GHZ + if (band == BAND_5GHZ) { if (!is5GhzBandSupported()) { return SoftApConfiguration.BAND_2GHZ; } - return SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_2GHZ; + // fallthrough to return BAND_5GHZ + } else if (band == BAND_6GHZ) { + if (!is6GhzBandSupported()) { + return SoftApConfiguration.BAND_2GHZ; + } + // fallthrough to return BAND_6GHZ } return band; @@ -118,16 +159,55 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen @VisibleForTesting void updatePreferenceEntries() { + final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); Resources res = mContext.getResources(); - int entriesRes = R.array.wifi_ap_band; - int summariesRes = R.array.wifi_ap_band_summary; - mBandEntries = res.getStringArray(entriesRes); - mBandSummaries = res.getStringArray(summariesRes); + ArrayList<String> bandEntries = new ArrayList<String>(); + ArrayList<String> bandSummaries = new ArrayList<String>(); + // Add 2GHz band + bandEntries.add(String.valueOf(SoftApConfiguration.BAND_2GHZ)); + bandSummaries.add(mContext.getString(R.string.wifi_ap_choose_2G)); + // Add 5GHz band + if (is5GhzBandSupported()) { + bandEntries.add(String.valueOf(BAND_5GHZ)); + bandSummaries.add(mContext.getString(R.string.wifi_ap_prefer_5G)); + } + // Add 6GHz band + if (is6GhzBandSupported()) { + bandEntries.add(String.valueOf(BAND_6GHZ)); + bandSummaries.add(mContext.getString(R.string.wifi_ap_prefer_6G)); + } + // Add Dual AP bands + if (is5GhzBandSupported() + && (mWifiManager.isBridgedApConcurrencySupported() || isVendorLegacyDualBandSupported()) + && (config != null) && (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OWE)) { + /* isConcurrentBandSupported indicates Concurrent band support + * for HW_SUPPORTED_FEATURES.*/ + if (mWifiManager.isConcurrentBandSupported()) { + bandEntries.add(String.valueOf(BAND_BOTH_2G_5G)); + bandSummaries.add(mContext.getString(R.string.wifi_ap_choose_vendor_dual_band)); + } + } + + mBandEntries = bandEntries.toArray(new String[bandEntries.size()]); + mBandSummaries = bandSummaries.toArray(new String[bandSummaries.size()]); + } + + // This is used to reduce IPC calls to framework. + private void syncBandSupportAndCountryCode() { + m5GHzSupported = mWifiManager.is5GHzBandSupported(); + m6GHzSupported = mWifiManager.is6GHzBandSupported(); + mCountryCode = mWifiManager.getCountryCode(); } private boolean is5GhzBandSupported() { - final String countryCode = mWifiManager.getCountryCode(); - if (!mWifiManager.is5GHzBandSupported() || countryCode == null) { + if (!m5GHzSupported || mCountryCode == null) { + return false; + } + return true; + } + + private boolean is6GhzBandSupported() { + if (!m6GHzSupported || mCountryCode == null) { return false; } return true; @@ -136,4 +216,9 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen public int getBandIndex() { return mBandIndex; } + + private boolean isVendorLegacyDualBandSupported() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wifi_dual_sap_mode_enabled); + } } diff --git a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java index b54a80f6b4..8ada0b3b32 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java @@ -22,13 +22,17 @@ import android.net.wifi.WifiManager; import androidx.preference.Preference; import androidx.preference.SwitchPreference; +import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; +import android.util.Log; public class WifiTetherAutoOffPreferenceController extends BasePreferenceController implements Preference.OnPreferenceChangeListener { private final WifiManager mWifiManager; + private String TAG = "WifiTetherAutoOffPreferenceController"; + private SwitchPreference mPreference = null; public WifiTetherAutoOffPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -41,6 +45,19 @@ public class WifiTetherAutoOffPreferenceController extends BasePreferenceControl } @Override + public void displayPreference(PreferenceScreen scr) { + super.displayPreference(scr); + mPreference = scr.findPreference(getPreferenceKey()); + } + + public void updateDisplay() { + if(mPreference != null) + updateState(mPreference); + else + Log.e(TAG, "updateDisplay Failed, cannot find switch preference"); + } + + @Override public void updateState(Preference preference) { SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration(); final boolean settingsOn = softApConfiguration.isAutoShutdownEnabled(); diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java deleted file mode 100644 index a1a10ea643..0000000000 --- a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2021 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.wifi.tether; - -import android.content.Context; -import android.net.wifi.SoftApConfiguration; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.SwitchPreference; - -import com.android.settings.R; - -/** - * This controller helps to manage the state of maximize compatibility switch preference. - */ -public class WifiTetherMaximizeCompatibilityPreferenceController extends - WifiTetherBasePreferenceController { - - private static final String TAG = "WifiTetherMaximizeCompatibilityPref"; - public static final String PREF_KEY = "wifi_tether_maximize_compatibility"; - - private boolean mIsChecked; - - public WifiTetherMaximizeCompatibilityPreferenceController(Context context, - WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { - super(context, listener); - mIsChecked = isMaximizeCompatibilityEnabled(); - } - - @Override - public String getPreferenceKey() { - return PREF_KEY; - } - - @Override - public void updateDisplay() { - if (mPreference == null) { - return; - } - mPreference.setEnabled(is5GhzBandSupported()); - ((SwitchPreference) mPreference).setChecked(mIsChecked); - mPreference.setSummary(mWifiManager.isBridgedApConcurrencySupported() - ? R.string.wifi_hotspot_maximize_compatibility_dual_ap_summary - : R.string.wifi_hotspot_maximize_compatibility_single_ap_summary); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - mIsChecked = (Boolean) newValue; - if (mListener != null) { - mListener.onTetherConfigUpdated(this); - } - return true; - } - - private boolean is5GhzBandSupported() { - if (mWifiManager == null) { - return false; - } - if (!mWifiManager.is5GHzBandSupported() || mWifiManager.getCountryCode() == null) { - return false; - } - return true; - } - - @VisibleForTesting - boolean isMaximizeCompatibilityEnabled() { - if (mWifiManager == null) { - return false; - } - final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); - if (config == null) { - return false; - } - if (mWifiManager.isBridgedApConcurrencySupported()) { - final boolean isEnabled = config.isBridgedModeOpportunisticShutdownEnabled(); - Log.d(TAG, "isBridgedModeOpportunisticShutdownEnabled:" + isEnabled); - // Because the return value defined by the Wi-Fi framework API is opposite to the UI. - // Compatibility on: isBridgedModeOpportunisticShutdownEnabled() = false - // Compatibility off: isBridgedModeOpportunisticShutdownEnabled() = true - // Need to return the reverse value. - return !isEnabled; - } - - // If the BridgedAp Concurrency is not supported in early Pixel devices (e.g. Pixel 2~5), - // show toggle on when band is 2.4G only. - final int band = config.getBand(); - Log.d(TAG, "getBand:" + band); - return band == SoftApConfiguration.BAND_2GHZ; - } - - /** - * Setup the Maximize Compatibility setting to the SoftAp Configuration - * - * @param builder The builder to build the SoftApConfiguration. - */ - public void setupMaximizeCompatibility(SoftApConfiguration.Builder builder) { - if (builder == null) { - return; - } - final boolean enabled = mIsChecked; - if (mWifiManager.isBridgedApConcurrencySupported()) { - int[] bands = { - SoftApConfiguration.BAND_2GHZ, - SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; - builder.setBands(bands); - Log.d(TAG, "setBridgedModeOpportunisticShutdownEnabled:" + enabled); - // Because the defined value by the Wi-Fi framework API is opposite to the UI. - // Compatibility on: setBridgedModeOpportunisticShutdownEnabled(false) - // Compatibility off: setBridgedModeOpportunisticShutdownEnabled(true) - // Need to set the reverse value. - builder.setBridgedModeOpportunisticShutdownEnabled(!enabled); - } else { - int band = enabled - ? SoftApConfiguration.BAND_2GHZ - : SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ; - Log.d(TAG, "setBand:" + band); - builder.setBand(band); - } - } -} diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java index 14766f9ee4..ec70f97b52 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java @@ -72,7 +72,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer @Override public void updateDisplay() { final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); - if (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN + if (!isNoPasswordSecurityrType(config.getSecurityType()) && TextUtils.isEmpty(config.getPassphrase())) { mPassword = generateRandomPassword(); } else { @@ -107,7 +107,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer */ public String getPasswordValidated(int securityType) { // don't actually overwrite unless we get a new config in case it was accidentally toggled. - if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) { + if (isNoPasswordSecurityrType(securityType)) { return ""; } else if (!WifiUtils.isHotspotPasswordValid(mPassword, securityType)) { mPassword = generateRandomPassword(); @@ -124,7 +124,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer */ public void setSecurityType(int securityType) { mSecurityType = securityType; - mPreference.setVisible(securityType != SoftApConfiguration.SECURITY_TYPE_OPEN); + mPreference.setVisible(!isNoPasswordSecurityrType(securityType)); } @Override @@ -151,4 +151,9 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer pref.setVisible(false); } } + + private boolean isNoPasswordSecurityrType(int securityType) { + return (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN + || securityType == SoftApConfiguration.SECURITY_TYPE_OWE); + } } diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java index 70b6a45eb5..70ac155974 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java @@ -130,10 +130,15 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController public void onConnectedClientsChanged(List<WifiClient> clients) { if (mPreference != null && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) { + String extendWifiSummary = ""; + if (mWifiManager.isExtendingWifi()) { + extendWifiSummary = "Extending Wifi-Coverage: "; + } + // Only show the number of clients when state is on - mPreference.setSummary(mContext.getResources().getQuantityString( - R.plurals.wifi_tether_connected_summary, clients.size(), - clients.size())); + mPreference.setSummary(extendWifiSummary + mContext.getResources() + .getQuantityString(R.plurals.wifi_tether_connected_summary, + clients.size(), clients.size())); } } }); diff --git a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java index acb8206249..0c209d491a 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java @@ -48,18 +48,23 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer private int mSecurityValue; @VisibleForTesting boolean mIsWpa3Supported = true; + boolean mIsOweSapSupported = true; + boolean mIsDualSapSupported = false; + private String[] securityNames; + private String[] securityValues; public WifiTetherSecurityPreferenceController(Context context, OnTetherConfigUpdateListener listener) { super(context, listener); - final String[] securityNames = mContext.getResources().getStringArray( + securityNames = mContext.getResources().getStringArray( R.array.wifi_tether_security); - final String[] securityValues = mContext.getResources().getStringArray( + securityValues = mContext.getResources().getStringArray( R.array.wifi_tether_security_values); for (int i = 0; i < securityNames.length; i++) { mSecurityMap.put(Integer.parseInt(securityValues[i]), securityNames[i]); } mWifiManager.registerSoftApCallback(context.getMainExecutor(), this); + mIsDualSapSupported = mWifiManager.isBridgedApConcurrencySupported(); } @Override @@ -78,17 +83,41 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer return; } final ListPreference preference = (ListPreference) mPreference; - // If the device is not support WPA3 then remove the WPA3 options. + final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + int defaultSecurityType = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; + + for (int i = 0; i < securityNames.length; i++) { + mSecurityMap.put(Integer.parseInt(securityValues[i]), securityNames[i]); + } + + preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new)); + preference.setEntryValues(mSecurityMap.keySet().stream().map(i -> Integer.toString(i)) + .toArray(CharSequence[]::new)); + + if (config.getBand() == (SoftApConfiguration.BAND_6GHZ | SoftApConfiguration.BAND_2GHZ) + && mSecurityMap.keySet().removeIf( + key -> key < SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)) { + preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new)); + preference.setEntryValues(mSecurityMap.keySet().stream().map(i -> Integer.toString(i)) + .toArray(CharSequence[]::new)); + defaultSecurityType = SoftApConfiguration.SECURITY_TYPE_WPA3_SAE; + } + // If the device does not support WPA3 /OWE then remove the WPA3 /OWE options. if (!mIsWpa3Supported && mSecurityMap.keySet() .removeIf(key -> key > SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)) { preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new)); - preference.setEntryValues(mSecurityMap.keySet().stream().map(Integer::toBinaryString) + preference.setEntryValues(mSecurityMap.keySet().stream().map(i -> Integer.toString(i)) + .toArray(CharSequence[]::new)); + } else if (!(mIsDualSapSupported && mIsOweSapSupported) && mSecurityMap.keySet() + .removeIf(key -> key > SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)) { + preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new)); + preference.setEntryValues(mSecurityMap.keySet().stream().map(i -> Integer.toString(i)) .toArray(CharSequence[]::new)); } final int securityType = mWifiManager.getSoftApConfiguration().getSecurityType(); mSecurityValue = mSecurityMap.get(securityType) != null - ? securityType : SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; + ? securityType : defaultSecurityType; preference.setSummary(mSecurityMap.get(mSecurityValue)); preference.setValue(String.valueOf(mSecurityValue)); @@ -111,8 +140,17 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer if (!isWpa3Supported) { Log.i(PREF_KEY, "WPA3 SAE is not supported on this device"); } - if (mIsWpa3Supported != isWpa3Supported) { + + final boolean isOweSupported = + softApCapability.areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_WPA3_OWE); + if (!isOweSupported) { + Log.i(PREF_KEY, "OWE not supported."); + } + + if (mIsWpa3Supported != isWpa3Supported + || mIsOweSapSupported != isOweSupported) { mIsWpa3Supported = isWpa3Supported; + mIsOweSapSupported = isOweSupported; updateDisplay(); } mWifiManager.unregisterSoftApCallback(this); @@ -121,4 +159,8 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer public int getSecurityType() { return mSecurityValue; } + + public boolean isOweDualSapSupported() { + return mIsDualSapSupported && mIsOweSapSupported; + } } diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 23601fac5d..02e0cd7025 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -18,6 +18,7 @@ package com.android.settings.wifi.tether; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION; +import static com.android.settings.wifi.tether.WifiTetherApBandPreferenceController.BAND_BOTH_2G_5G; import android.app.settings.SettingsEnums; import android.content.BroadcastReceiver; @@ -54,6 +55,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment private static final String TAG = "WifiTetherSettings"; private static final IntentFilter TETHER_STATE_CHANGE_FILTER; private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; + private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3; + private static boolean mWasApBand6GHzSelected = false; + private static final int BAND_6GHZ = SoftApConfiguration.BAND_6GHZ | SoftApConfiguration.BAND_2GHZ; @VisibleForTesting static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; @@ -62,18 +66,18 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @VisibleForTesting static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; @VisibleForTesting - static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = - WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; + static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; private WifiTetherPasswordPreferenceController mPasswordPreferenceController; + private WifiTetherApBandPreferenceController mApBandPreferenceController; private WifiTetherSecurityPreferenceController mSecurityPreferenceController; - private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; private WifiManager mWifiManager; private boolean mRestartWifiApAfterConfigChange; private boolean mUnavailable; + private boolean wasApBandPrefUpdated = false; @VisibleForTesting TetherChangeReceiver mTetherChangeReceiver; @@ -115,8 +119,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); - mMaxCompatibilityPrefController = - use(WifiTetherMaximizeCompatibilityPreferenceController.class); + mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); } @Override @@ -180,15 +183,16 @@ public class WifiTetherSettings extends RestrictedDashboardFragment controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); + controllers.add(new WifiTetherApBandPreferenceController(context, listener)); controllers.add( new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); - controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener)); + return controllers; } @Override public void onTetherConfigUpdated(AbstractPreferenceController context) { - final SoftApConfiguration config = buildNewConfig(); + SoftApConfiguration config = buildNewConfig(); mPasswordPreferenceController.setSecurityType(config.getSecurityType()); /** @@ -203,18 +207,59 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mSwitchBarController.stopTether(); } mWifiManager.setSoftApConfiguration(config); + use(WifiTetherAutoOffPreferenceController.class).updateDisplay(); + + if (mSecurityPreferenceController.isOweDualSapSupported()) { + if ((config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OWE) + && (mApBandPreferenceController.getBandIndex() == BAND_BOTH_2G_5G)) { + mApBandPreferenceController.updatePreferenceEntries(); + mApBandPreferenceController.updateDisplay(); + wasApBandPrefUpdated = true; + } else if (wasApBandPrefUpdated + && config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OWE) { + mApBandPreferenceController.updatePreferenceEntries(); + mApBandPreferenceController.updateDisplay(); + wasApBandPrefUpdated = false; + } + } + + if (mApBandPreferenceController.getBandIndex() == BAND_6GHZ + && (mWasApBand6GHzSelected == false)) { + mSecurityPreferenceController.updateDisplay(); + mWasApBand6GHzSelected = true; + config = buildNewConfig(); + mWifiManager.setSoftApConfiguration(config); + } else if (mApBandPreferenceController.getBandIndex() != BAND_6GHZ + &&(mWasApBand6GHzSelected == true)) { + mSecurityPreferenceController.updateDisplay(); + mWasApBand6GHzSelected = false; + } } private SoftApConfiguration buildNewConfig() { final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); final int securityType = mSecurityPreferenceController.getSecurityType(); configBuilder.setSsid(mSSIDPreferenceController.getSSID()); - if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) { + if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN + || securityType == SoftApConfiguration.SECURITY_TYPE_OWE) { + configBuilder.setPassphrase(null, securityType); + } else { configBuilder.setPassphrase( mPasswordPreferenceController.getPasswordValidated(securityType), securityType); } - mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder); + if (mApBandPreferenceController.getBandIndex() == BAND_BOTH_2G_5G) { + // Fallback to 2G band if user selected OWE+Dual band + if (securityType == SoftApConfiguration.SECURITY_TYPE_OWE) { + configBuilder.setBand(SoftApConfiguration.BAND_2GHZ); + } else { + int[] dualBands = new int[] { + SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ}; + configBuilder.setBands(dualBands); + } + } else { + configBuilder.setBand(mApBandPreferenceController.getBandIndex()); + } return configBuilder.build(); } @@ -224,10 +269,14 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } private void updateDisplayWithNewConfig() { - use(WifiTetherSSIDPreferenceController.class).updateDisplay(); - use(WifiTetherSecurityPreferenceController.class).updateDisplay(); - use(WifiTetherPasswordPreferenceController.class).updateDisplay(); - use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay(); + use(WifiTetherSSIDPreferenceController.class) + .updateDisplay(); + use(WifiTetherSecurityPreferenceController.class) + .updateDisplay(); + use(WifiTetherPasswordPreferenceController.class) + .updateDisplay(); + use(WifiTetherApBandPreferenceController.class) + .updateDisplay(); } public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = @@ -241,7 +290,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment keys.add(KEY_WIFI_TETHER_NETWORK_NAME); keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); keys.add(KEY_WIFI_TETHER_AUTO_OFF); - keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); } // Remove duplicate diff --git a/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceControllerTest.java index 7dd2906941..0f01e00a3f 100644 --- a/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothCodecDialogPreferenceControllerTest.java @@ -19,6 +19,7 @@ package com.android.settings.development.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -108,6 +109,8 @@ public class BluetoothCodecDialogPreferenceControllerTest { BluetoothCodecConfig[] mCodecConfigs = {mCodecConfigAAC, mCodecConfigSBC}; mCodecStatus = new BluetoothCodecStatus(mCodecConfigSBC, null, mCodecConfigs); when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus); + when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice)).thenReturn( + BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); mController.onBluetoothServiceConnected(mBluetoothA2dp); mController.writeConfigurationValues(0); @@ -172,4 +175,37 @@ public class BluetoothCodecDialogPreferenceControllerTest { verify(mCallback).onBluetoothCodecChanged(); } + + @Test + public void onHDAudioEnabled_optionalCodecEnabled_setsCodecTypeAsAAC() { + BluetoothCodecConfig[] mCodecConfigs = {mCodecConfigAAC, mCodecConfigSBC}; + mCodecStatus = new BluetoothCodecStatus(mCodecConfigAAC, + /* codecsLocalCapabilities= */ null, + mCodecConfigs); + when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus); + when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice)).thenReturn( + BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); + mController.onBluetoothServiceConnected(mBluetoothA2dp); + + mController.onHDAudioEnabled(/* enabled= */ true); + + verify(mBluetoothA2dpConfigStore, atLeastOnce()).setCodecType( + eq(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC)); + } + @Test + public void onHDAudioEnabled_optionalCodecDisabled_setsCodecTypeAsSBC() { + BluetoothCodecConfig[] mCodecConfigs = {mCodecConfigAAC, mCodecConfigSBC}; + mCodecStatus = new BluetoothCodecStatus(mCodecConfigAAC, + /* codecsLocalCapabilities= */ null, + mCodecConfigs); + when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus); + when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice)).thenReturn( + BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED); + mController.onBluetoothServiceConnected(mBluetoothA2dp); + + mController.onHDAudioEnabled(/* enabled= */ false); + + verify(mBluetoothA2dpConfigStore, atLeastOnce()).setCodecType( + eq(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java index ff0f25c338..edb2c3c477 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java @@ -353,6 +353,38 @@ public final class ConvertUtilsTest { } @Test + public void testGetIndexedUsageMap_hideBackgroundUsageTime_returnsExpectedResult() { + final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L}; + final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>(); + final BatteryHistEntry fakeEntry = createBatteryHistEntry( + ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L); + // Adds the index = 0 data. + Map<String, BatteryHistEntry> entryMap = new HashMap<>(); + entryMap.put(fakeEntry.getKey(), fakeEntry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); + // Adds the index = 1 data. + entryMap = new HashMap<>(); + entryMap.put(fakeEntry.getKey(), fakeEntry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap); + // Adds the index = 2 data. + entryMap = new HashMap<>(); + final BatteryHistEntry entry = createBatteryHistEntry( + "package3", "label3", 500, 5L, 3600000L, 7200000L); + entryMap.put(entry.getKey(), entry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); + when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeList(mContext)) + .thenReturn(Arrays.asList((CharSequence) "package3")); + + final Map<Integer, List<BatteryDiffEntry>> purgedResultMap = + ConvertUtils.getIndexedUsageMap( + mContext, /*timeSlotSize=*/ 1, batteryHistoryKeys, batteryHistoryMap, + /*purgeLowPercentageAndFakeData=*/ true); + + final BatteryDiffEntry resultEntry = purgedResultMap.get(0).get(0); + assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0); + } + + @Test public void getLocale_nullContext_returnDefaultLocale() { assertThat(ConvertUtils.getLocale(/*context=*/ null)) .isEqualTo(Locale.getDefault()); diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java index 5f2f5647c5..def4169697 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java @@ -277,6 +277,8 @@ public class WifiSettingsTest { @Test public void onWifiEntriesChanged_shouldChangeNextButtonState() { + final FragmentActivity activity = mock(FragmentActivity.class); + doReturn(activity).when(mWifiSettings).getActivity(); mWifiSettings.onWifiEntriesChanged(); verify(mWifiSettings).changeNextButtonState(anyBoolean()); diff --git a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java index 796cdef434..78e696d641 100644 --- a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java @@ -39,7 +39,7 @@ import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.os.Bundle; import android.view.MenuItem; - +import android.view.LayoutInflater; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -110,30 +110,30 @@ public class WifiP2pSettingsTest { } @Test - public void onActivityCreate_withNullBundle_canNotGetValue() { - mFragment.onActivityCreated(null); + public void onCreateView_withNullBundle_canNotGetValue() { + mFragment.onCreateView(LayoutInflater.from(mContext), null, null); assertThat(mFragment.mSelectedWifiPeer).isNull(); } @Test - public void onActivityCreate_withDeviceName_shouldGetDeviceName() { + public void onCreateView_withDeviceName_shouldGetDeviceName() { final String fakeDeviceName = "fakename"; final Bundle bundle = new Bundle(); bundle.putString(WifiP2pSettings.SAVE_DEVICE_NAME, fakeDeviceName); - mFragment.onActivityCreated(bundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, bundle); assertThat(mFragment.mSavedDeviceName).isEqualTo(fakeDeviceName); } @Test - public void onActivityCreate_withGroupName_shouldGetGroupName() { + public void onCreateView_withGroupName_shouldGetGroupName() { final String fakeGroupName = "fakegroup"; final Bundle bundle = new Bundle(); bundle.putString(WifiP2pSettings.SAVE_SELECTED_GROUP, fakeGroupName); - mFragment.onActivityCreated(bundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, bundle); assertThat(mFragment.mSelectedGroupName).isEqualTo(fakeGroupName); assertThat(mFragment.mSavedDeviceName).isNull(); @@ -279,7 +279,7 @@ public class WifiP2pSettingsTest { final String fakeDeviceName = "fakeName"; final Bundle bundle = new Bundle(); bundle.putString(WifiP2pSettings.SAVE_DEVICE_NAME, fakeDeviceName); - mFragment.onActivityCreated(bundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, bundle); final Dialog dialog = mFragment.onCreateDialog(WifiP2pSettings.DIALOG_RENAME); mFragment.mRenameListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE); @@ -292,7 +292,8 @@ public class WifiP2pSettingsTest { final String fakeDeviceName = "wrongName***"; final Bundle bundle = new Bundle(); bundle.putString(WifiP2pSettings.SAVE_DEVICE_NAME, fakeDeviceName); - mFragment.onActivityCreated(bundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, bundle); + final Dialog dialog = mFragment.onCreateDialog(WifiP2pSettings.DIALOG_RENAME); mFragment.mRenameListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE); @@ -394,7 +395,7 @@ public class WifiP2pSettingsTest { final String fakeDeviceName = "fakeName"; final Bundle createBundle = new Bundle(); createBundle.putString(WifiP2pSettings.SAVE_DEVICE_NAME, fakeDeviceName); - mFragment.onActivityCreated(createBundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, createBundle); final Bundle outBundle = new Bundle(); final Dialog dialog = mFragment.onCreateDialog(WifiP2pSettings.DIALOG_RENAME); @@ -430,7 +431,7 @@ public class WifiP2pSettingsTest { doReturn(groupList).when(wifiP2pGroupList).getGroupList(); final Bundle bundle = new Bundle(); bundle.putString(WifiP2pSettings.SAVE_SELECTED_GROUP, fakeGroupName); - mFragment.onActivityCreated(bundle); + mFragment.onCreateView(LayoutInflater.from(mContext), null, bundle); mFragment.onPersistentGroupInfoAvailable(wifiP2pGroupList); @@ -503,20 +504,20 @@ public class WifiP2pSettingsTest { } @Test - public void onActivityCreate_withNullP2pManager_shouldGetP2pManagerAgain() { - mFragment.mChannel = null; // Reset channel to re-test onActivityCreated flow + public void onCreateView_withNullP2pManager_shouldGetP2pManagerAgain() { + mFragment.mChannel = null; // Reset channel to re-test onCreateView flow mFragment.mWifiP2pManager = null; - mFragment.onActivityCreated(new Bundle()); + mFragment.onCreateView(LayoutInflater.from(mContext), null, new Bundle()); assertThat(mFragment.mWifiP2pManager).isNotNull(); } @Test - public void onActivityCreate_withNullChannel_shouldSetP2pManagerNull() { + public void onCreateView_withNullChannel_shouldSetP2pManagerNull() { doReturn(null).when(mWifiP2pManager).initialize(any(), any(), any()); - mFragment.mChannel = null; // Reset channel to re-test onActivityCreated flow - mFragment.onActivityCreated(new Bundle()); + mFragment.mChannel = null; // Reset channel to re-test onCreateView flow + mFragment.onCreateView(LayoutInflater.from(mContext), null, new Bundle()); assertThat(mFragment.mWifiP2pManager).isNull(); } diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index 2ecc7d26d5..a241296191 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -101,7 +101,7 @@ public class WifiTetherSettingsTest { assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); } @Test @@ -115,7 +115,7 @@ public class WifiTetherSettingsTest { assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); } @Test diff --git a/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java b/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java index d00e2dd891..91257c1d0c 100644 --- a/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java +++ b/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java @@ -22,12 +22,14 @@ import static org.mockito.Mockito.spy; import android.content.Context; import android.os.Looper; +import android.util.FeatureFlagUtils; import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -51,6 +53,7 @@ public class NetworkProviderCallsSmsFragmentTest { } } + @Ignore @Test @UiThreadTest public void isPageSearchEnabled_shouldIncludeFragmentXml() { diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java index 8687e5ada6..20821a0f2b 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java @@ -224,8 +224,9 @@ public class ProviderModelSliceHelperTest { public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable { mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "", false, true); - when(mConnectivityManager.getActiveNetwork()).thenReturn(null); Drawable expectDrawable = mock(Drawable.class); + when(mConnectivityManager.getActiveNetwork()).thenReturn(null); + when(mTelephonyManager.isDataEnabled()).thenReturn(false); assertThat(mProviderModelSliceHelper.getMobileDrawable(expectDrawable)).isEqualTo( expectDrawable); @@ -236,8 +237,9 @@ public class ProviderModelSliceHelperTest { throws Throwable { mockConnections(true, ServiceState.STATE_IN_SERVICE, "", true, true); - addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); Drawable drawable = mock(Drawable.class); + addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + when(mTelephonyManager.isDataEnabled()).thenReturn(true); assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo( mDrawableWithSignalStrength); @@ -252,6 +254,7 @@ public class ProviderModelSliceHelperTest { true); Drawable drawable = mock(Drawable.class); addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI); + when(mTelephonyManager.isDataEnabled()).thenReturn(true); assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo( mDrawableWithSignalStrength); diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java index 66247d3623..44643decbb 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java @@ -65,6 +65,7 @@ import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiPickerTracker; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -485,6 +486,7 @@ public class ProviderModelSliceTest { verify(mMockAlertDialog, never()).show(); } + @Ignore @Test public void doCarrierNetworkAction_toggleActionSetDataEnabled_setCarrierNetworkEnabledTrue() { mMockProviderModelSlice.doCarrierNetworkAction(true /* isToggleAction */, @@ -493,6 +495,7 @@ public class ProviderModelSliceTest { verify(mMockNetworkProviderWorker).setCarrierNetworkEnabledIfNeeded(true, SUB_ID); } + @Ignore @Test public void doCarrierNetworkAction_toggleActionSetDataDisabled_setCarrierNetworkEnabledFalse() { mMockProviderModelSlice.doCarrierNetworkAction(true /* isToggleAction */, @@ -501,6 +504,7 @@ public class ProviderModelSliceTest { verify(mMockNetworkProviderWorker).setCarrierNetworkEnabledIfNeeded(false, SUB_ID); } + @Ignore @Test public void doCarrierNetworkAction_primaryActionAndDataEnabled_connectCarrierNetwork() { mMockProviderModelSlice.doCarrierNetworkAction(false /* isToggleAction */, diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java index f046c9a6f2..a8acbe5348 100644 --- a/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/NetworkScanHelperTest.java @@ -92,7 +92,7 @@ public class NetworkScanHelperTest { mNetworkScanExecutor = Executors.newFixedThreadPool(1); - mNetworkScanHelper = new NetworkScanHelper(mTelephonyManager, + mNetworkScanHelper = new NetworkScanHelper(null, mTelephonyManager, mNetworkScanCallback, mNetworkScanExecutor); mNetworkScan = spy(new NetworkScanMock(SCAN_ID, SUB_ID)); diff --git a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java index 6bca9ab892..5eb9513767 100644 --- a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java @@ -37,6 +37,7 @@ import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import androidx.lifecycle.Lifecycle; import androidx.preference.ListPreference; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -62,6 +63,8 @@ public class PreferredNetworkModePreferenceControllerTest { private CarrierConfigManager mCarrierConfigManager; @Mock private ServiceState mServiceState; + @Mock + private Lifecycle mLifecycle; private PersistableBundle mPersistableBundle; private PreferredNetworkModePreferenceController mController; @@ -87,7 +90,7 @@ public class PreferredNetworkModePreferenceControllerTest { mPreference = new ListPreference(mContext); mController = new PreferredNetworkModePreferenceController(mContext, "mobile_data"); - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); mPreference.setKey(mController.getPreferenceKey()); } diff --git a/tests/unit/src/com/android/settings/panel/PanelFeatureProviderImplTest.java b/tests/unit/src/com/android/settings/panel/PanelFeatureProviderImplTest.java index a02c054237..952dd51ce5 100644 --- a/tests/unit/src/com/android/settings/panel/PanelFeatureProviderImplTest.java +++ b/tests/unit/src/com/android/settings/panel/PanelFeatureProviderImplTest.java @@ -33,6 +33,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java deleted file mode 100644 index 3d8b24c5f2..0000000000 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2021 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.wifi.tether; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.net.wifi.SoftApConfiguration; -import android.net.wifi.WifiManager; -import android.os.Looper; - -import androidx.preference.PreferenceManager; -import androidx.preference.PreferenceScreen; -import androidx.preference.SwitchPreference; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.testutils.ResourcesUtils; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@RunWith(AndroidJUnit4.class) -public class WifiTetherMaximizeCompatibilityPreferenceControllerTest { - - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock - private WifiManager mWifiManager; - @Mock - private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener; - - private Context mContext; - private WifiTetherMaximizeCompatibilityPreferenceController mController; - private SwitchPreference mPreference; - private SoftApConfiguration mConfig; - - @Before - public void setUp() { - mContext = spy(ApplicationProvider.getApplicationContext()); - mConfig = new SoftApConfiguration.Builder() - .setSsid("test_Ssid") - .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN) - .setBridgedModeOpportunisticShutdownEnabled(true) - .build(); - doReturn(mWifiManager).when(mContext).getSystemService(Context.WIFI_SERVICE); - doReturn(true).when(mWifiManager).isBridgedApConcurrencySupported(); - doReturn(mConfig).when(mWifiManager).getSoftApConfiguration(); - - mController = new WifiTetherMaximizeCompatibilityPreferenceController(mContext, mListener); - if (Looper.myLooper() == null) { - Looper.prepare(); - } - final PreferenceManager preferenceManager = new PreferenceManager(mContext); - final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); - mPreference = new SwitchPreference(mContext); - mPreference.setKey(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); - screen.addPreference(mPreference); - mController.displayPreference(screen); - } - - @Test - public void getPreferenceKey_shouldBeCorrect() { - assertThat(mController.getPreferenceKey()) - .isEqualTo(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); - } - - @Test - public void updateDisplay_notSupport5GHzBand_setPreferenceDisabled() { - doReturn(false).when(mWifiManager).is5GHzBandSupported(); - - mController.updateDisplay(); - - assertThat(mPreference.isEnabled()).isEqualTo(false); - } - - @Test - public void updateDisplay_getNullCountryCode_setPreferenceDisabled() { - doReturn(null).when(mWifiManager).getCountryCode(); - - mController.updateDisplay(); - - assertThat(mPreference.isEnabled()).isEqualTo(false); - } - - @Test - public void updateDisplay_notSupportedBridgedApConcurrency_setSingleApSummary() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - - mController.updateDisplay(); - - assertThat(mPreference.getSummary()).isEqualTo(ResourcesUtils.getResourcesString(mContext, - "wifi_hotspot_maximize_compatibility_single_ap_summary")); - } - - @Test - public void updateDisplay_supportedBridgedApConcurrency_setDualApSummary() { - doReturn(true).when(mWifiManager).isBridgedApConcurrencySupported(); - - mController.updateDisplay(); - - assertThat(mPreference.getSummary()).isEqualTo(ResourcesUtils.getResourcesString(mContext, - "wifi_hotspot_maximize_compatibility_dual_ap_summary")); - } - - @Test - public void updateDisplay_supported5GHzBandAndCountryCodeIsNotNull_setPreferenceEnabled() { - doReturn(true).when(mWifiManager).is5GHzBandSupported(); - doReturn("US").when(mWifiManager).getCountryCode(); - - mController.updateDisplay(); - - assertThat(mPreference.isEnabled()).isEqualTo(true); - } - - @Test - public void onPreferenceChange_callbackOnTetherConfigUpdated() { - mController.onPreferenceChange(mPreference, true); - verify(mListener).onTetherConfigUpdated(any()); - } - - @Test - public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnFalse() { - // The preconditions are ready in setup(). - - assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); - } - - @Test - public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnTrue() { - SoftApConfiguration config = new SoftApConfiguration.Builder() - .setBridgedModeOpportunisticShutdownEnabled(false) - .build(); - doReturn(config).when(mWifiManager).getSoftApConfiguration(); - - assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); - } - - @Test - public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gOnly_returnFalse() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - SoftApConfiguration config = new SoftApConfiguration.Builder() - .setBand(SoftApConfiguration.BAND_2GHZ) - .build(); - doReturn(config).when(mWifiManager).getSoftApConfiguration(); - - assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); - } - - @Test - public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand5gOnly_returnTrue() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - SoftApConfiguration config = new SoftApConfiguration.Builder() - .setBand(SoftApConfiguration.BAND_5GHZ) - .build(); - doReturn(config).when(mWifiManager).getSoftApConfiguration(); - - assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); - } - - @Test - public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gAnd5g_returnTrue() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - SoftApConfiguration config = new SoftApConfiguration.Builder() - .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ) - .build(); - doReturn(config).when(mWifiManager).getSoftApConfiguration(); - - assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); - } - - @Test - public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setEnabled() { - // The precondition of the concurrency supported is ready in setup(). - mController.onPreferenceChange(mPreference, false); - - SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); - mController.setupMaximizeCompatibility(builder); - - assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(true); - } - - @Test - public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setDisabled() { - // The precondition of the concurrency supported is ready in setup(). - mController.onPreferenceChange(mPreference, true); - - SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); - mController.setupMaximizeCompatibility(builder); - - assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(false); - } - - @Test - public void setupMaximizeCompatibility_noConcurrencyAndSetDisabled_setBand2gOnly() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - mController.onPreferenceChange(mPreference, false); - - SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); - mController.setupMaximizeCompatibility(builder); - - assertThat(builder.build().getBand()) - .isEqualTo(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ); - } - - @Test - public void setupMaximizeCompatibility_noConcurrencyAndSetEnabled_setBand2gAnd5g() { - doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); - mController.onPreferenceChange(mPreference, true); - - SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); - mController.setupMaximizeCompatibility(builder); - - assertThat(builder.build().getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); - } -} |