Snap for 5056300 from 7243a4016b to qt-release
Change-Id: Icc4162ba453b717ad0de593f3447d96afb9a4a85
This commit is contained in:
@@ -131,6 +131,13 @@
|
||||
android:launchMode="singleTask">
|
||||
</activity>
|
||||
|
||||
<activity android:name=".mobilenetwork.MobileSettingsActivity"
|
||||
android:label="@string/network_settings_title"
|
||||
android:theme="@style/Theme.Settings.Home"
|
||||
android:launchMode="singleTask">
|
||||
<!-- TODO(b/114749736): add intent filter here and disable the one in telephony -->
|
||||
</activity>
|
||||
|
||||
<!-- Alias for launcher activity only, as this belongs to each profile. -->
|
||||
<activity-alias android:name="Settings"
|
||||
android:taskAffinity="com.android.settings.root"
|
||||
@@ -3029,9 +3036,6 @@
|
||||
android:exported="true"
|
||||
android:permission="android.permission.DUMP" />
|
||||
|
||||
<service android:name=".search.DeviceIndexUpdateJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<!-- Quick Settings tiles for Developer Options -->
|
||||
<service
|
||||
android:name=".development.qstile.DevelopmentTiles$ShowLayout"
|
||||
|
||||
@@ -1501,22 +1501,6 @@
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardCodedColor"
|
||||
severity="Error"
|
||||
message="Avoid using hardcoded color"
|
||||
category="Correctness"
|
||||
priority="4"
|
||||
summary="Using hardcoded color"
|
||||
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
|
||||
errorLine1=" settings:fillColorSecondary="#ff80cbc4""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="res/layout/data_usage_chart.xml"
|
||||
line="47"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardCodedColor"
|
||||
severity="Error"
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<!-- NOTE: this explicitly uses right/left padding, since the
|
||||
graph isn't swapped in RTL languages -->
|
||||
<com.android.settings.widget.ChartDataUsageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/data_usage_chart_height"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<com.android.settings.widget.ChartGridView
|
||||
android:id="@+id/grid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|bottom"
|
||||
android:paddingBottom="24dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Caption"
|
||||
settings:borderDrawable="@drawable/data_grid_border" />
|
||||
|
||||
<com.android.settings.widget.ChartNetworkSeriesView
|
||||
android:id="@+id/series"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|bottom"
|
||||
settings:strokeColor="#00000000"
|
||||
settings:fillColor="?android:attr/colorAccent"
|
||||
settings:fillColorSecondary="#ff80cbc4"
|
||||
settings:safeRegion="3dp" />
|
||||
|
||||
<com.android.settings.widget.ChartNetworkSeriesView
|
||||
android:id="@+id/detail_series"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|bottom"
|
||||
settings:strokeColor="#00000000"
|
||||
settings:fillColor="?android:attr/colorAccent"
|
||||
settings:fillColorSecondary="?android:attr/colorAccent"
|
||||
settings:safeRegion="3dp" />
|
||||
|
||||
<com.android.settings.widget.ChartSweepView
|
||||
android:id="@+id/sweep_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusUp="@+id/sweep_limit"
|
||||
settings:sweepDrawable="@drawable/data_sweep_warning"
|
||||
settings:followAxis="vertical"
|
||||
settings:neighborMargin="5dip"
|
||||
settings:labelSize="60dip"
|
||||
settings:labelTemplate="@string/data_usage_sweep_warning"
|
||||
settings:labelColor="?android:attr/textColorSecondary"
|
||||
settings:safeRegion="4dp" />
|
||||
|
||||
<com.android.settings.widget.ChartSweepView
|
||||
android:id="@+id/sweep_limit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusDown="@+id/sweep_warning"
|
||||
settings:sweepDrawable="@drawable/data_sweep_limit"
|
||||
settings:followAxis="vertical"
|
||||
settings:neighborMargin="5dip"
|
||||
settings:labelSize="60dip"
|
||||
settings:labelTemplate="@string/data_usage_sweep_limit"
|
||||
settings:labelColor="?android:attr/colorError"
|
||||
settings:safeRegion="4dp" />
|
||||
|
||||
</com.android.settings.widget.ChartDataUsageView>
|
||||
50
res/layout/mobile_settings_container.xml
Normal file
50
res/layout/mobile_settings_container.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Toolbar
|
||||
android:id="@+id/action_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:navigationContentDescription="@*android:string/action_bar_up_description"
|
||||
android:theme="?android:attr/actionBarTheme"
|
||||
style="?android:attr/actionBarStyle"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottom_nav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_marginStart="0dp"
|
||||
android:background="?android:attr/windowBackground"
|
||||
app:itemIconTint="@color/bottom_navigation_colors"
|
||||
app:itemTextColor="@color/bottom_navigation_colors"
|
||||
app:menu="@menu/home_bottom_navigation"/>
|
||||
</LinearLayout>
|
||||
@@ -1133,4 +1133,186 @@
|
||||
<item>4</item> <!-- AutofillManager.FLAG_ADD_CLIENT_VERBOSE -->
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_choices" translatable="false">
|
||||
<item>@string/network_lte</item>
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_2G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_4g_choices" translatable="false">
|
||||
<item>@string/network_4G</item>
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_2G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_values" translatable="false">
|
||||
<item>"9"</item>
|
||||
<item>"0"</item>
|
||||
<item>"1"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="cdma_system_select_choices">
|
||||
<!-- System select dialog screen, setting option name -->
|
||||
<item>Home only</item>
|
||||
<!-- Remove the following option "Affiliated Networks" from the option list -->
|
||||
<!-- <item>Affiliated Networks</item> -->
|
||||
<!-- System select dialog screen, setting option name -->
|
||||
<item>Automatic</item>
|
||||
</string-array>
|
||||
<string-array name="cdma_system_select_values" translatable="false">
|
||||
<!-- Do not translate. -->
|
||||
<item>"0"</item>
|
||||
<!-- Remove the following value "1" which corresponds to "Affiliated Networks" above -->
|
||||
<!-- <item>"1"</item> -->
|
||||
<!-- Do not translate. -->
|
||||
<item>"2"</item>
|
||||
</string-array>
|
||||
|
||||
<!-- The preferred network modes in Mobile network settings -->
|
||||
<string-array name="preferred_network_mode_choices">
|
||||
<item>GSM/WCDMA preferred</item>
|
||||
<item>GSM only</item>
|
||||
<item>WCDMA only</item>
|
||||
<item>GSM/WCDMA auto</item>
|
||||
<item>CDMA/EvDo auto</item>
|
||||
<item>CDMA w/o EvDo</item>
|
||||
<item>EvDo only</item>
|
||||
<item>CDMA/EvDo/GSM/WCDMA</item>
|
||||
<item>CDMA + LTE/EvDo</item>
|
||||
<item>GSM/WCDMA/LTE</item>
|
||||
<item>Global</item>
|
||||
<item>LTE</item>
|
||||
<item>LTE / WCDMA</item>
|
||||
<item>TDSCDMA only</item>
|
||||
<item>TDSCDMA/WCDMA</item>
|
||||
<item>LTE/TDSCDMA</item>
|
||||
<item>TDSCDMA/GSM</item>
|
||||
<item>LTE/TDSCDMA/GSM</item>
|
||||
<item>TDSCDMA/GSM/WCDMA</item>
|
||||
<item>LTE/TDSCDMA/WCDMA</item>
|
||||
<item>LTE/TDSCDMA/GSM/WCDMA</item>
|
||||
<item>TDSCDMA/CDMA/EVDO/GSM/WCDMA </item>
|
||||
<item>LTE/TDSCDMA/CDMA/EVDO/GSM/WCDMA</item>
|
||||
</string-array>
|
||||
<!-- The preferred network modes RIL constants, in order of the modes above,
|
||||
e.g. the choice "GSM/WCDMA preferred" has the corresponding value "0" -->
|
||||
<string-array name="preferred_network_mode_values" translatable="false">
|
||||
<item>"0"</item>
|
||||
<item>"1"</item>
|
||||
<item>"2"</item>
|
||||
<item>"3"</item>
|
||||
<item>"4"</item>
|
||||
<item>"5"</item>
|
||||
<item>"6"</item>
|
||||
<item>"7"</item>
|
||||
<item>"8"</item>
|
||||
<item>"9"</item>
|
||||
<item>"10"</item>
|
||||
<item>"11"</item>
|
||||
<item>"12"</item>
|
||||
<item>"13"</item>
|
||||
<item>"14"</item>
|
||||
<item>"15"</item>
|
||||
<item>"16"</item>
|
||||
<item>"17"</item>
|
||||
<item>"18"</item>
|
||||
<item>"19"</item>
|
||||
<item>"20"</item>
|
||||
<item>"21"</item>
|
||||
<item>"22"</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Choices for CDMA subscription-->
|
||||
<string-array name="cdma_subscription_choices">
|
||||
<item>RUIM/SIM</item>
|
||||
<item>NV</item>
|
||||
</string-array>
|
||||
<!-- Values for CDMA subscription-->
|
||||
<string-array name="cdma_subscription_values">
|
||||
<item>"0"</item>
|
||||
<item>"1"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_except_gsm_choices" translatable="false">
|
||||
<item>@string/network_lte</item>
|
||||
<item>@string/network_3G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_except_gsm_4g_choices" translatable="false">
|
||||
<item>@string/network_4G</item>
|
||||
<item>@string/network_3G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_except_gsm_values" translatable="false">
|
||||
<item>"9"</item>
|
||||
<item>"0"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_except_lte_choices" translatable="false">
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_2G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_except_lte_values" translatable="false">
|
||||
<item>"0"</item>
|
||||
<item>"1"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_except_gsm_lte_choices" translatable="false">
|
||||
<item>@string/network_3G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_except_gsm_lte_values" translatable="false">
|
||||
<item>"0"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_cdma_choices" translatable="false">
|
||||
<item>@string/network_lte</item>
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_1x</item>
|
||||
<item>@string/network_global</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_cdma_values" translatable="false">
|
||||
<item>"8"</item>
|
||||
<item>"4"</item>
|
||||
<item>"5"</item>
|
||||
<item>"10"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_cdma_no_lte_choices" translatable="false">
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_1x</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_cdma_no_lte_values" translatable="false">
|
||||
<item>"4"</item>
|
||||
<item>"5"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_cdma_only_lte_choices" translatable="false">
|
||||
<item>@string/network_lte</item>
|
||||
<item>@string/network_global</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_cdma_only_lte_values" translatable="false">
|
||||
<item>"8"</item>
|
||||
<item>"10"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="enabled_networks_tdscdma_choices" translatable="false">
|
||||
<item>@string/network_lte</item>
|
||||
<item>@string/network_3G</item>
|
||||
<item>@string/network_2G</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_networks_tdscdma_values" translatable="false">
|
||||
<item>"22"</item>
|
||||
<item>"18"</item>
|
||||
<item>"1"</item>
|
||||
</string-array>
|
||||
|
||||
<!--String arrays for World preferred netwrok modes -->
|
||||
<string-array name="preferred_network_mode_choices_world_mode">
|
||||
<item>Global</item>
|
||||
<item>LTE / CDMA</item>
|
||||
<item>LTE / GSM / UMTS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="preferred_network_mode_values_world_mode">
|
||||
<item>"10"</item>
|
||||
<item>"8"</item>
|
||||
<item>"9"</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -50,13 +50,6 @@
|
||||
<attr name="android:textAppearance" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ChartNetworkSeriesView">
|
||||
<attr name="strokeColor" format="color" />
|
||||
<attr name="fillColor" format="color" />
|
||||
<attr name="fillColorSecondary" format="color" />
|
||||
<attr name="safeRegion" />
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="apnPreferenceStyle" format="reference" />
|
||||
|
||||
<attr name="footerPreferenceStyle" format="reference" />
|
||||
|
||||
@@ -136,4 +136,13 @@
|
||||
|
||||
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
|
||||
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
|
||||
|
||||
<!-- TODO(b/115429501): move those 3 configs to framework-->
|
||||
<!-- Show enabled lte option for lte device -->
|
||||
<bool name="config_enabled_lte" translatable="false">false</bool>
|
||||
<!-- Show enabled tdscdma option for device -->
|
||||
<bool name="config_support_tdscdma" translatable="false">false</bool>
|
||||
<!-- Show enabled tdscdma option for device when connect roaming network -->
|
||||
<string-array name="config_support_tdscdma_roaming_on_networks"
|
||||
translatable="false"></string-array>
|
||||
</resources>
|
||||
|
||||
@@ -6971,6 +6971,9 @@
|
||||
<!-- Search keyword for Ambient display settings screen. -->
|
||||
<string name="keywords_ambient_display_screen">Ambient display, Lock screen display</string>
|
||||
|
||||
<!-- Search keyword for lock screen notification setting [CHAR_LIMIT=NONE] -->
|
||||
<string name="keywords_lock_screen_notif">lock screen notification</string>
|
||||
|
||||
<!-- Search keyword for face settings. -->
|
||||
<string name="keywords_face_settings">face</string>
|
||||
|
||||
@@ -10112,4 +10115,146 @@
|
||||
|
||||
<!-- Message informs the user that has no SIM card in personalized Settings [CHAR LIMIT=30] -->
|
||||
<string name="no_sim_card">No SIM card</string>
|
||||
|
||||
<!-- Carrier variant of Enhaced 4G LTE Mode title. [CHAR LIMIT=50] -->
|
||||
<string name="enhanced_4g_lte_mode_title_variant">Advanced Calling</string>
|
||||
<!-- The following strings are summaries for preferred network modes in Mobile network settings,
|
||||
and have a character limit of 100 -->
|
||||
<!-- WCDMA preferred [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_wcdma_perf_summary">Preferred network mode: WCDMA preferred</string>
|
||||
<!-- GSM only [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_gsm_only_summary">Preferred network mode: GSM only</string>
|
||||
<!-- WCDMA only [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_wcdma_only_summary">Preferred network mode: WCDMA only</string>
|
||||
<!-- GSM / WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_gsm_wcdma_summary">Preferred network mode: GSM / WCDMA</string>
|
||||
<!-- CDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_cdma_summary">Preferred network mode: CDMA</string>
|
||||
<!-- CDMA / EvDo [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_cdma_evdo_summary">Preferred network mode: CDMA / EvDo</string>
|
||||
<!-- CDMA only [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_cdma_only_summary">Preferred network mode: CDMA only</string>
|
||||
<!-- EvDo only [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_evdo_only_summary">Preferred network mode: EvDo only</string>
|
||||
<!-- CDMA/EvDo/GSM/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_cdma_evdo_gsm_wcdma_summary">Preferred network mode: CDMA/EvDo/GSM/WCDMA</string>
|
||||
<!-- LTE [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_summary">Preferred network mode: LTE </string>
|
||||
<!-- GSM/WCDMA/LTE [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_gsm_wcdma_summary">Preferred network mode: GSM/WCDMA/LTE</string>
|
||||
<!-- CDMA+LTE/EVDO [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_cdma_evdo_summary">Preferred network mode: CDMA+LTE/EVDO</string>
|
||||
<!-- Global [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_global_summary">Preferred network mode: Global</string>
|
||||
<!-- LTE / WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_wcdma_summary">Preferred network mode: LTE / WCDMA</string>
|
||||
<!-- LTE / GSM / UMTS [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_gsm_umts_summary">Preferred network mode: LTE / GSM / UMTS</string>
|
||||
<!-- LTE / CDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_cdma_summary">Preferred network mode: LTE / CDMA</string>
|
||||
<!-- TDSCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_tdscdma_summary">Preferred network mode: TDSCDMA</string>
|
||||
<!-- TDSCDMA / WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_tdscdma_wcdma_summary">Preferred network mode: TDSCDMA / WCDMA</string>
|
||||
<!-- LTE / TDSCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_tdscdma_summary">Preferred network mode: LTE / TDSCDMA</string>
|
||||
<!-- TDSCDMA / GSM [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_tdscdma_gsm_summary">Preferred network mode: TDSCDMA / GSM</string>
|
||||
<!-- LTE/GSM/TDSCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_tdscdma_gsm_summary">Preferred network mode: LTE/GSM/TDSCDMA</string>
|
||||
<!-- TDSCDMA/GSM/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_tdscdma_gsm_wcdma_summary">Preferred network mode: TDSCDMA/GSM/WCDMA</string>
|
||||
<!-- LTE/TDSCDMA/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_tdscdma_wcdma_summary">Preferred network mode: LTE/TDSCDMA/WCDMA</string>
|
||||
<!-- LTE/TDSCDMA/GSM/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_tdscdma_gsm_wcdma_summary">Preferred network mode: LTE/TDSCDMA/GSM/WCDMA</string>
|
||||
<!-- TDSCDMA/CDMA/EvDo/GSM/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary">Preferred network mode: TDSCDMA/CDMA/EvDo/GSM/WCDMA</string>
|
||||
<!-- LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary">Preferred network mode: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA</string>
|
||||
|
||||
<!-- Text for Network lte [CHAR LIMIT=NONE] -->
|
||||
<string name="network_lte">LTE (recommended)</string>
|
||||
<!-- Text for Network 4g [CHAR LIMIT=NONE] -->
|
||||
<string name="network_4G">4G (recommended)</string>
|
||||
<!-- Text for Network 3g [CHAR LIMIT=NONE] -->
|
||||
<string name="network_3G" translatable="false">3G</string>
|
||||
<!-- Text for Network 2g [CHAR LIMIT=NONE] -->
|
||||
<string name="network_2G" translatable="false">2G</string>
|
||||
<!-- Text for Network 1x [CHAR LIMIT=NONE] -->
|
||||
<string name="network_1x" translatable="false">1x</string>
|
||||
<!-- Text for Network global [CHAR LIMIT=NONE] -->
|
||||
<string name="network_global">Global</string>
|
||||
|
||||
<!-- Configuration setting for world mode Format is <true;GID if any to be checked> [CHAR LIMIT=NONE] -->
|
||||
<string translatable="false" name="config_world_mode"/>
|
||||
|
||||
<!-- Available networks screen title/heading [CHAR LIMIT=NONE] -->
|
||||
<string name="label_available">Available networks</string>
|
||||
<!-- Mobile network settings screen, toast when searching for available networks [CHAR LIMIT=NONE] -->
|
||||
<string name="load_networks_progress">Searching\u2026</string>
|
||||
<!-- Available networks screen, toast when registering on a specific network [CHAR LIMIT=NONE] -->
|
||||
<string name="register_on_network">Registering on <xliff:g id="network" example="Verizon">%s</xliff:g>\u2026</string>
|
||||
<!-- Available networks screen, toast when SIM card isn't allowed on a network [CHAR LIMIT=NONE] -->
|
||||
<string name="not_allowed">Your SIM card doesn\u2019t allow a connection to this network.</string>
|
||||
<!-- Available networks screen, toast when unable to connect to a network temporarily [CHAR LIMIT=NONE] -->
|
||||
<string name="connect_later">Can\u2019t connect to this network right now. Try again later.</string>
|
||||
<!-- Available networks screen, toast when registered on a specific network [CHAR LIMIT=NONE] -->
|
||||
<string name="registration_done">Registered on network.</string>
|
||||
|
||||
<!-- Available networks screen, name of switch button for whether to select network automatically [CHAR LIMIT=NONE] -->
|
||||
<string name="select_automatically">Automatically select network</string>
|
||||
|
||||
<!-- A menu item in "Mobile network settings" that allows the user to update the phone
|
||||
with mobile operator specific settings. [CHAR LIMIT=25] -->
|
||||
<string name="carrier_settings_title">Carrier settings</string>
|
||||
<!-- Preference title for launching an account manager page for prepaid LTE [CHAR LIMIT=30] -->
|
||||
<string name="cdma_lte_data_service">Set up data service</string>
|
||||
<!-- Mobile network settings screen, title of Mobile Data switch preference [CHAR LIMIT=NONE] -->
|
||||
<string name="mobile_data_settings_title">Mobile data</string>
|
||||
<!-- Mobile network settings screen, title of Mobile data switch preference [CHAR LIMIT=NONE] -->
|
||||
<string name="mobile_data_settings_summary">Access data using mobile network</string>
|
||||
|
||||
<!-- Title for preferred network type [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_title">Preferred network type</string>
|
||||
<!-- Summary for preferred network type [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_summary">Change the network operating mode</string>
|
||||
<!-- Dialog title for preferred network type [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_dialogtitle">Preferred network type</string>
|
||||
<!-- Mobile network settings screen, name of the option to manage carrier profiles on devices which support embedded carrier profiles [CHAR LIMIT=NONE] -->
|
||||
<string name="carrier_settings_euicc">Carrier</string>
|
||||
|
||||
<!-- Mobile network settings screen, name for call settings category [CHAR LIMIT=NONE] -->
|
||||
<string name="call_category">Calling</string>
|
||||
<!-- Mobile network settings screen, title of Video calling setting [CHAR LIMIT=NONE] -->
|
||||
<string name="video_calling_settings_title">Carrier video calling</string>
|
||||
|
||||
<!-- Mobile network settings screen, setting option name [CHAR LIMIT=NONE] -->
|
||||
<string name="cdma_system_select_title">System select</string>
|
||||
<!-- Mobile network settings screen, setting summary text [CHAR LIMIT=NONE] -->
|
||||
<string name="cdma_system_select_summary">Change the CDMA roaming mode</string>
|
||||
<!-- System select settings screen title [CHAR LIMIT=NONE] -->
|
||||
<string name="cdma_system_select_dialogtitle">System select</string>
|
||||
|
||||
<!-- Mobile network settings screen, name for network operator category [CHAR LIMIT=NONE] -->
|
||||
<string name="network_operator_category">Network</string>
|
||||
<!-- Available networks screen, name of button when user wants to select network manually [CHAR LIMIT=NONE] -->
|
||||
<string name="network_select_title">Network</string>
|
||||
|
||||
<!-- Title for CDMA subscription [CHAR LIMIT=50] -->
|
||||
<string name="cdma_subscription_title">CDMA subscription</string>
|
||||
<!-- Summary for CDMA subscription [CHAR LIMIT=NONE] -->
|
||||
<string name="cdma_subscription_summary">Change between RUIM/SIM and NV</string>
|
||||
<!-- Dialog title for CDMA subscription [CHAR LIMIT=30] -->
|
||||
<string name="cdma_subscription_dialogtitle">subscription</string>
|
||||
|
||||
<!-- Title to register automatically [CHAR LIMIT=NONE] -->
|
||||
<string name="register_automatically">Automatic registration\u2026</string>
|
||||
<!-- Mobile network settings screen, dialog message title when user selects the Data roaming check box [CHAR LIMIT=120] -->
|
||||
<string name="roaming_alert_title">Allow data roaming?</string>
|
||||
<!-- Mobile network settings screen, message asking the user to check their pricing with their Carrier, when enabling Data roaming. [CHAR LIMIT=NONE] -->
|
||||
<string name="roaming_check_price_warning">Check with your network provider for pricing.</string>
|
||||
|
||||
<!-- 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>
|
||||
</resources>
|
||||
|
||||
53
res/xml/cdma_options.xml
Normal file
53
res/xml/cdma_options.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2008 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">
|
||||
|
||||
<com.android.settings.mobilenetwork.CdmaSystemSelectListPreference
|
||||
android:key="cdma_system_select_key"
|
||||
android:title="@string/cdma_system_select_title"
|
||||
android:summary="@string/cdma_system_select_summary"
|
||||
android:entries="@array/cdma_system_select_choices"
|
||||
android:entryValues="@array/cdma_system_select_values"
|
||||
android:dialogTitle="@string/cdma_system_select_dialogtitle" />
|
||||
|
||||
<com.android.settings.mobilenetwork.CdmaSubscriptionListPreference
|
||||
android:key="cdma_subscription_key"
|
||||
android:title="@string/cdma_subscription_title"
|
||||
android:summary="@string/cdma_subscription_summary"
|
||||
android:entries="@array/cdma_subscription_choices"
|
||||
android:entryValues="@array/cdma_subscription_values"
|
||||
android:dialogTitle="@string/cdma_subscription_dialogtitle" />
|
||||
|
||||
<!--We want separate APN setting from reset of settings because-->
|
||||
<!--we want user to change it with caution.-->
|
||||
<PreferenceCategory
|
||||
android:key="category_cdma_apn_key">
|
||||
<!-- The launching Intent will be defined thru code as we need to pass some Extra -->
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="button_cdma_apn_key"
|
||||
android:title="@string/apn_settings"
|
||||
android:persistent="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<Preference
|
||||
android:key="carrier_settings_key"
|
||||
android:title="@string/carrier_settings_title">
|
||||
<!-- b/114749736, create a preference controller to build intent -->
|
||||
</Preference>
|
||||
|
||||
</PreferenceScreen>
|
||||
59
res/xml/gsm_umts_options.xml
Normal file
59
res/xml/gsm_umts_options.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2008 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">
|
||||
|
||||
<com.android.settings.mobilenetwork.NetworkOperators
|
||||
android:key="network_operators_category_key"
|
||||
android:title="@string/network_operator_category"
|
||||
android:persistent="false">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="button_auto_select_key"
|
||||
android:title="@string/select_automatically"
|
||||
android:persistent="false"/>
|
||||
|
||||
<com.android.settings.mobilenetwork.NetworkSelectListPreference
|
||||
android:key="button_network_select_key"
|
||||
android:title="@string/network_select_title"
|
||||
android:persistent="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="button_choose_network_key"
|
||||
android:title="@string/choose_network_title"
|
||||
android:fragment="com.android.phone.NetworkSelectSetting" />
|
||||
</com.android.settings.mobilenetwork.NetworkOperators>
|
||||
|
||||
<!--We want separate APN setting from reset of settings because-->
|
||||
<!--we want user to change it with caution.-->
|
||||
<PreferenceCategory
|
||||
android:key="category_gsm_apn_key"
|
||||
android:layout="@layout/preference_category_no_label">
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="button_gsm_apn_key"
|
||||
android:title="@string/apn_settings"
|
||||
android:persistent="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="carrier_settings_key"
|
||||
android:title="@string/carrier_settings_title">
|
||||
<!-- b/114749736, create a preference controller to build intent -->
|
||||
</PreferenceScreen>
|
||||
|
||||
</PreferenceScreen>
|
||||
91
res/xml/network_setting_fragment.xml
Normal file
91
res/xml/network_setting_fragment.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 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"
|
||||
settings:initialExpandedChildrenCount="4">
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="cdma_lte_data_service_key"
|
||||
android:title="@string/cdma_lte_data_service">
|
||||
</PreferenceScreen>
|
||||
|
||||
<com.android.settings.mobilenetwork.MobileDataPreference
|
||||
android:key="mobile_data_enable"
|
||||
android:title="@string/mobile_data_settings_title"
|
||||
android:summary="@string/mobile_data_settings_summary"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="button_roaming_key"
|
||||
android:title="@string/roaming"
|
||||
android:persistent="false"
|
||||
android:summaryOn="@string/roaming_enable"
|
||||
android:summaryOff="@string/roaming_disable"/>
|
||||
|
||||
<com.android.settings.mobilenetwork.DataUsagePreference
|
||||
android:key="data_usage_summary"
|
||||
android:title="@string/data_usage_title" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="enhanced_4g_lte"
|
||||
android:title="@string/enhanced_4g_lte_mode_title"
|
||||
android:persistent="false"
|
||||
android:summary="@string/enhanced_4g_lte_mode_summary"/>
|
||||
|
||||
<ListPreference
|
||||
android:key="preferred_network_mode_key"
|
||||
android:title="@string/preferred_network_mode_title"
|
||||
android:summary="@string/preferred_network_mode_summary"
|
||||
android:entries="@array/preferred_network_mode_choices"
|
||||
android:entryValues="@array/preferred_network_mode_values"
|
||||
android:dialogTitle="@string/preferred_network_mode_dialogtitle" />
|
||||
|
||||
<ListPreference
|
||||
android:key="enabled_networks_key"
|
||||
android:title="@string/preferred_network_mode_title"
|
||||
android:summary="@string/preferred_network_mode_summary"
|
||||
android:entries="@array/enabled_networks_choices"
|
||||
android:entryValues="@array/enabled_networks_values"
|
||||
android:dialogTitle="@string/preferred_network_mode_dialogtitle" />
|
||||
|
||||
<Preference
|
||||
android:key="carrier_settings_euicc_key"
|
||||
android:title="@string/carrier_settings_euicc" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="calling"
|
||||
android:title="@string/call_category">
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="wifi_calling_key"
|
||||
android:title="@string/wifi_calling_settings_title">
|
||||
|
||||
<intent android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.android.settings"
|
||||
android:targetClass="com.android.settings.Settings$WifiCallingSettingsActivity">
|
||||
<extra android:name="show_drawer_menu" android:value="true" />
|
||||
</intent>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<SwitchPreference
|
||||
android:key="video_calling_key"
|
||||
android:title="@string/video_calling_settings_title"
|
||||
android:persistent="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -27,7 +27,8 @@
|
||||
<com.android.settings.RestrictedListPreference
|
||||
android:key="security_setting_lock_screen_notif"
|
||||
android:title="@string/lock_screen_notifications_title"
|
||||
android:summary="@string/summary_placeholder" />
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:keywords="@string/keywords_lock_screen_notif"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="security_lockscreen_add_users_when_locked"
|
||||
|
||||
@@ -65,14 +65,12 @@ import com.android.settings.dashboard.DashboardSummary;
|
||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
||||
import com.android.settings.homepage.TopLevelSettings;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.DeviceIndexFeatureProvider;
|
||||
import com.android.settings.wfd.WifiDisplaySettings;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settingslib.core.instrumentation.Instrumentable;
|
||||
import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -514,7 +512,6 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
|
||||
updateTilesList();
|
||||
updateDeviceIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -636,14 +633,6 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDeviceIndex() {
|
||||
DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(
|
||||
this).getDeviceIndexFeatureProvider();
|
||||
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> indexProvider.updateIndex(SettingsActivity.this, false /* force */));
|
||||
}
|
||||
|
||||
private void doUpdateTilesList() {
|
||||
PackageManager pm = getPackageManager();
|
||||
final UserManager um = UserManager.get(this);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -32,7 +33,6 @@ import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
import com.android.settings.password.StorageManagerWrapper;
|
||||
|
||||
public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
|
||||
private static final String KEY_LOCK_SCREEN_PRESENT = "wasLockScreenPresent";
|
||||
@@ -59,7 +59,7 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu
|
||||
protected Intent getChooseLockIntent() {
|
||||
Intent intent = new Intent(this, SetupChooseLockGeneric.class);
|
||||
|
||||
if (StorageManagerWrapper.isFileEncryptedNativeOrEmulated()) {
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
intent.putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
|
||||
@@ -14,18 +14,15 @@
|
||||
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
||||
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
|
||||
import static android.net.TrafficStats.UID_REMOVED;
|
||||
import static android.net.TrafficStats.UID_TETHERING;
|
||||
import static android.telephony.TelephonyManager.SIM_STATE_READY;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.INetworkStatsSession;
|
||||
import android.net.NetworkPolicy;
|
||||
import android.net.NetworkStats;
|
||||
@@ -35,13 +32,11 @@ import android.net.TrafficStats;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.content.pm.UserInfo;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkPolicy;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@@ -252,8 +251,7 @@ public class DataUsageListV2 extends DataUsageBaseFragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update body content based on current tab. Loads
|
||||
* {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
|
||||
* Update body content based on current tab. Loads network cycle data from system, and
|
||||
* binds them to visible controls.
|
||||
*/
|
||||
private void updateBody() {
|
||||
|
||||
@@ -51,8 +51,7 @@ public class DataUsagePreference extends Preference implements TemplatePreferenc
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplate(NetworkTemplate template, int subId,
|
||||
NetworkServices services) {
|
||||
public void setTemplate(NetworkTemplate template, int subId, NetworkServices services) {
|
||||
mTemplate = template;
|
||||
mSubId = subId;
|
||||
final DataUsageController controller = getDataUsageController();
|
||||
@@ -66,7 +65,7 @@ public class DataUsagePreference extends Preference implements TemplatePreferenc
|
||||
DataUsageUtils.formatDataUsage(getContext(), usageInfo.usageLevel),
|
||||
usageInfo.period));
|
||||
}
|
||||
final long usageLevel = controller.getHistoriclUsageLevel(template);
|
||||
final long usageLevel = controller.getHistoricalUsageLevel(template);
|
||||
if (usageLevel > 0L) {
|
||||
setIntent(getIntent());
|
||||
} else {
|
||||
|
||||
@@ -283,8 +283,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage
|
||||
formatUsedData()));
|
||||
} else {
|
||||
final DataUsageController.DataUsageInfo info =
|
||||
mDataController
|
||||
.getDataUsageInfo(NetworkTemplate.buildTemplateWifiWildcard());
|
||||
mDataController.getWifiDataUsageInfo();
|
||||
|
||||
if (info == null) {
|
||||
mSummaryLoader.setSummary(this, null);
|
||||
|
||||
@@ -178,7 +178,7 @@ public class DataUsageSummaryPreference extends Preference {
|
||||
carrierInfo.setVisibility(View.GONE);
|
||||
limitInfo.setVisibility(View.GONE);
|
||||
|
||||
final long usageLevel = getHistoriclUsageLevel();
|
||||
final long usageLevel = getHistoricalUsageLevel();
|
||||
if (usageLevel > 0L) {
|
||||
launchButton.setOnClickListener((view) -> {
|
||||
launchWifiDataUsage(getContext());
|
||||
@@ -339,9 +339,9 @@ public class DataUsageSummaryPreference extends Preference {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
long getHistoriclUsageLevel() {
|
||||
long getHistoricalUsageLevel() {
|
||||
final DataUsageController controller = new DataUsageController(getContext());
|
||||
return controller.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
|
||||
return controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
204
src/com/android/settings/mobilenetwork/CdmaOptions.java
Normal file
204
src/com/android/settings/mobilenetwork/CdmaOptions.java
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.telephony.PhoneConstants;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
/**
|
||||
* List of Phone-specific settings screens.
|
||||
*/
|
||||
public class CdmaOptions {
|
||||
private static final String LOG_TAG = "CdmaOptions";
|
||||
|
||||
private CarrierConfigManager mCarrierConfigManager;
|
||||
private CdmaSystemSelectListPreference mButtonCdmaSystemSelect;
|
||||
private CdmaSubscriptionListPreference mButtonCdmaSubscription;
|
||||
private RestrictedPreference mButtonAPNExpand;
|
||||
private Preference mCategoryAPNExpand;
|
||||
private Preference mButtonCarrierSettings;
|
||||
|
||||
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 static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key";
|
||||
private static final String BUTTON_APN_EXPAND_KEY = "button_cdma_apn_key";
|
||||
private static final String CATEGORY_APN_EXPAND_KEY = "category_cdma_apn_key";
|
||||
|
||||
private PreferenceFragmentCompat mPrefFragment;
|
||||
private PreferenceScreen mPrefScreen;
|
||||
private int mSubId;
|
||||
|
||||
public CdmaOptions(PreferenceFragmentCompat prefFragment, PreferenceScreen prefScreen, int subId) {
|
||||
mPrefFragment = prefFragment;
|
||||
mPrefScreen = prefScreen;
|
||||
mPrefFragment.addPreferencesFromResource(R.xml.cdma_options);
|
||||
mCarrierConfigManager = new CarrierConfigManager(prefFragment.getContext());
|
||||
|
||||
// Initialize preferences.
|
||||
mButtonCdmaSystemSelect = (CdmaSystemSelectListPreference) mPrefScreen
|
||||
.findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY);
|
||||
mButtonCdmaSubscription = (CdmaSubscriptionListPreference) mPrefScreen
|
||||
.findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY);
|
||||
mButtonCarrierSettings = mPrefScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY);
|
||||
mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY);
|
||||
mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY);
|
||||
|
||||
updateSubscriptionId(subId);
|
||||
}
|
||||
|
||||
protected void updateSubscriptionId(int subId) {
|
||||
mSubId = subId;
|
||||
int phoneType = TelephonyManager.from(mPrefFragment.getContext())
|
||||
.createForSubscriptionId(mSubId).getPhoneType();
|
||||
|
||||
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
|
||||
// Some CDMA carriers want the APN settings.
|
||||
boolean addAPNExpand = shouldAddApnExpandPreference(phoneType, carrierConfig);
|
||||
boolean addCdmaSubscription =
|
||||
deviceSupportsNvAndRuim();
|
||||
// Read platform settings for carrier settings
|
||||
boolean addCarrierSettings =
|
||||
carrierConfig.getBoolean(CarrierConfigManager.KEY_CARRIER_SETTINGS_ENABLE_BOOL);
|
||||
|
||||
mPrefScreen.addPreference(mButtonCdmaSystemSelect);
|
||||
mButtonCdmaSystemSelect.setEnabled(true);
|
||||
|
||||
// Making no assumptions of whether they are added or removed at this point.
|
||||
// Calling add or remove explicitly to make sure they are updated.
|
||||
|
||||
if (addAPNExpand) {
|
||||
log("update: addAPNExpand");
|
||||
mButtonAPNExpand.setDisabledByAdmin(
|
||||
MobileNetworkUtils.isDpcApnEnforced(mButtonAPNExpand.getContext())
|
||||
? RestrictedLockUtilsInternal.getDeviceOwner(
|
||||
mButtonAPNExpand.getContext())
|
||||
: null);
|
||||
mButtonAPNExpand.setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
MetricsLogger.action(mButtonAPNExpand.getContext(),
|
||||
MetricsEvent.ACTION_MOBILE_NETWORK_APN_SETTINGS);
|
||||
// We need to build the Intent by hand as the Preference Framework
|
||||
// does not allow to add an Intent with some extras into a Preference
|
||||
// XML file
|
||||
final Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);
|
||||
// This will setup the Home and Search affordance
|
||||
intent.putExtra(":settings:show_fragment_as_subsetting", true);
|
||||
intent.putExtra("sub_id", mSubId);
|
||||
mPrefFragment.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mPrefScreen.addPreference(mCategoryAPNExpand);
|
||||
} else {
|
||||
mPrefScreen.removePreference(mCategoryAPNExpand);
|
||||
}
|
||||
|
||||
if (addCdmaSubscription) {
|
||||
log("Both NV and Ruim supported, ENABLE subscription type selection");
|
||||
mPrefScreen.addPreference(mButtonCdmaSubscription);
|
||||
mButtonCdmaSubscription.setEnabled(true);
|
||||
} else {
|
||||
log("Both NV and Ruim NOT supported, REMOVE subscription type selection");
|
||||
mPrefScreen.removePreference(mButtonCdmaSubscription);
|
||||
}
|
||||
|
||||
if (addCarrierSettings) {
|
||||
mPrefScreen.addPreference(mButtonCarrierSettings);
|
||||
} else {
|
||||
mPrefScreen.removePreference(mButtonCarrierSettings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether we should add the APN expandable preference based on the phone type and
|
||||
* carrier config
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static boolean shouldAddApnExpandPreference(int phoneType, PersistableBundle config) {
|
||||
return phoneType == PhoneConstants.PHONE_TYPE_CDMA
|
||||
&& config.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL);
|
||||
}
|
||||
|
||||
private boolean deviceSupportsNvAndRuim() {
|
||||
// retrieve the list of subscription types supported by device.
|
||||
String subscriptionsSupported = SystemProperties.get("ril.subscription.types");
|
||||
boolean nvSupported = false;
|
||||
boolean ruimSupported = false;
|
||||
|
||||
log("deviceSupportsnvAnRum: prop=" + subscriptionsSupported);
|
||||
if (!TextUtils.isEmpty(subscriptionsSupported)) {
|
||||
// Searches through the comma-separated list for a match for "NV"
|
||||
// and "RUIM" to update nvSupported and ruimSupported.
|
||||
for (String subscriptionType : subscriptionsSupported.split(",")) {
|
||||
subscriptionType = subscriptionType.trim();
|
||||
if (subscriptionType.equalsIgnoreCase("NV")) {
|
||||
nvSupported = true;
|
||||
}
|
||||
if (subscriptionType.equalsIgnoreCase("RUIM")) {
|
||||
ruimSupported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log("deviceSupportsnvAnRum: nvSupported=" + nvSupported +
|
||||
" ruimSupported=" + ruimSupported);
|
||||
return (nvSupported && ruimSupported);
|
||||
}
|
||||
|
||||
public boolean preferenceTreeClick(Preference preference) {
|
||||
if (preference.getKey().equals(BUTTON_CDMA_SYSTEM_SELECT_KEY)) {
|
||||
log("preferenceTreeClick: return BUTTON_CDMA_ROAMING_KEY true");
|
||||
return true;
|
||||
}
|
||||
if (preference.getKey().equals(BUTTON_CDMA_SUBSCRIPTION_KEY)) {
|
||||
log("preferenceTreeClick: return CDMA_SUBSCRIPTION_KEY true");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showDialog(Preference preference) {
|
||||
if (preference.getKey().equals(BUTTON_CDMA_SYSTEM_SELECT_KEY)) {
|
||||
mButtonCdmaSystemSelect.showDialog(null);
|
||||
} else if (preference.getKey().equals(BUTTON_CDMA_SUBSCRIPTION_KEY)) {
|
||||
mButtonCdmaSubscription.showDialog(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void log(String s) {
|
||||
android.util.Log.d(LOG_TAG, s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.preference.ListPreference;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.telephony.Phone;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
public class CdmaSubscriptionListPreference extends ListPreference {
|
||||
|
||||
private static final String LOG_TAG = "CdmaSubListPref";
|
||||
|
||||
// Used for CDMA subscription mode
|
||||
private static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
|
||||
private static final int CDMA_SUBSCRIPTION_NV = 1;
|
||||
|
||||
//preferredSubscriptionMode 0 - RUIM/SIM, preferred
|
||||
// 1 - NV
|
||||
static final int preferredSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION;
|
||||
|
||||
private TelephonyManager mTelephonyManager;
|
||||
|
||||
public CdmaSubscriptionListPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mTelephonyManager = TelephonyManager.from(context);
|
||||
setCurrentCdmaSubscriptionModeValue();
|
||||
}
|
||||
|
||||
private void setCurrentCdmaSubscriptionModeValue() {
|
||||
int cdmaSubscriptionMode = Settings.Global.getInt(getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_SUBSCRIPTION_MODE, preferredSubscriptionMode);
|
||||
setValue(Integer.toString(cdmaSubscriptionMode));
|
||||
}
|
||||
|
||||
public CdmaSubscriptionListPreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subscription id associated with this preference.
|
||||
*
|
||||
* @param subId the subscription id.
|
||||
*/
|
||||
public void setSubscriptionId(int subId) {
|
||||
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId);
|
||||
}
|
||||
|
||||
//TODO(b/114749736): move this logic to preference controller
|
||||
protected void showDialog(Bundle state) {
|
||||
setCurrentCdmaSubscriptionModeValue();
|
||||
}
|
||||
|
||||
//TODO(b/114749736): move this logic to preference controller
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
if (!positiveResult) {
|
||||
//The button was dismissed - no need to set new value
|
||||
return;
|
||||
}
|
||||
|
||||
int buttonCdmaSubscriptionMode = Integer.parseInt(getValue());
|
||||
Log.d(LOG_TAG, "Setting new value " + buttonCdmaSubscriptionMode);
|
||||
int statusCdmaSubscriptionMode;
|
||||
switch(buttonCdmaSubscriptionMode) {
|
||||
case CDMA_SUBSCRIPTION_NV:
|
||||
statusCdmaSubscriptionMode = Phone.CDMA_SUBSCRIPTION_NV;
|
||||
break;
|
||||
case CDMA_SUBSCRIPTION_RUIM_SIM:
|
||||
statusCdmaSubscriptionMode = Phone.CDMA_SUBSCRIPTION_RUIM_SIM;
|
||||
break;
|
||||
default:
|
||||
statusCdmaSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION;
|
||||
}
|
||||
|
||||
// Set the CDMA subscription mode, when mode has been successfully changed, update the
|
||||
// mode to the global setting.
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
// The subscription mode selected by user.
|
||||
int cdmaSubscriptionMode = Integer.parseInt(getValue());
|
||||
|
||||
boolean isSuccessed = mTelephonyManager.setCdmaSubscriptionMode(
|
||||
statusCdmaSubscriptionMode);
|
||||
|
||||
// Update the global settings if successed.
|
||||
if (isSuccessed) {
|
||||
Settings.Global.putInt(getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_SUBSCRIPTION_MODE,
|
||||
cdmaSubscriptionMode);
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Setting Cdma subscription source failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
|
||||
public class CdmaSystemSelectListPreference extends ListPreference {
|
||||
|
||||
private static final String LOG_TAG = "CdmaRoamingListPref";
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private MyHandler mHandler = new MyHandler();
|
||||
|
||||
public CdmaSystemSelectListPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mHandler = new MyHandler();
|
||||
mTelephonyManager = TelephonyManager.from(context);
|
||||
}
|
||||
|
||||
public CdmaSystemSelectListPreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subscription id associated with this preference.
|
||||
*
|
||||
* @param subId the subscription id.
|
||||
*/
|
||||
public void setSubscriptionId(int subId) {
|
||||
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId);
|
||||
queryCdmaRoamingMode();
|
||||
}
|
||||
|
||||
//TODO(b/114749736): Move this to preference controller
|
||||
protected void showDialog(Bundle state) {
|
||||
if (!mTelephonyManager.getEmergencyCallbackMode()) {
|
||||
// show Dialog
|
||||
}
|
||||
}
|
||||
|
||||
//TODO(b/114749736): Move this to preference controller
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
if (positiveResult && (getValue() != null)) {
|
||||
int buttonCdmaRoamingMode = Integer.parseInt(getValue());
|
||||
int settingsCdmaRoamingMode = Settings.Global.getInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
TelephonyManager.CDMA_ROAMING_MODE_HOME);
|
||||
if (buttonCdmaRoamingMode != settingsCdmaRoamingMode) {
|
||||
int cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_ANY;
|
||||
if (buttonCdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_ANY) {
|
||||
cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_HOME;
|
||||
}
|
||||
//Set the Settings.Secure network mode
|
||||
Settings.Global.putInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
buttonCdmaRoamingMode);
|
||||
//Set the roaming preference mode
|
||||
setCdmaRoamingMode(cdmaRoamingMode);
|
||||
}
|
||||
} else {
|
||||
Log.d(LOG_TAG, String.format("onDialogClosed: positiveResult=%b value=%s -- do nothing",
|
||||
positiveResult, getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private class MyHandler extends Handler {
|
||||
|
||||
static final int MESSAGE_GET_ROAMING_PREFERENCE = 0;
|
||||
static final int MESSAGE_SET_ROAMING_PREFERENCE = 1;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_GET_ROAMING_PREFERENCE:
|
||||
handleQueryCdmaRoamingPreference(msg);
|
||||
break;
|
||||
|
||||
case MESSAGE_SET_ROAMING_PREFERENCE:
|
||||
handleSetCdmaRoamingPreference(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleQueryCdmaRoamingPreference(Message msg) {
|
||||
int cdmaRoamingMode = msg.arg1;
|
||||
|
||||
if (cdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT) {
|
||||
int settingsRoamingMode = Settings.Global.getInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
TelephonyManager.CDMA_ROAMING_MODE_HOME);
|
||||
|
||||
//check that statusCdmaRoamingMode is from an accepted value
|
||||
if (cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_HOME
|
||||
|| cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_ANY) {
|
||||
//check changes in statusCdmaRoamingMode and updates settingsRoamingMode
|
||||
if (cdmaRoamingMode != settingsRoamingMode) {
|
||||
settingsRoamingMode = cdmaRoamingMode;
|
||||
//changes the Settings.Secure accordingly to statusCdmaRoamingMode
|
||||
Settings.Global.putInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
settingsRoamingMode);
|
||||
}
|
||||
//changes the mButtonPreferredNetworkMode accordingly to modemNetworkMode
|
||||
setValue(Integer.toString(cdmaRoamingMode));
|
||||
}
|
||||
else {
|
||||
if(DBG) Log.i(LOG_TAG, "reset cdma roaming mode to default" );
|
||||
resetCdmaRoamingModeToDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetCdmaRoamingPreference(Message msg) {
|
||||
boolean isSuccessed = (boolean) msg.obj;
|
||||
|
||||
if (isSuccessed && (getValue() != null)) {
|
||||
int cdmaRoamingMode = Integer.parseInt(getValue());
|
||||
Settings.Global.putInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
cdmaRoamingMode );
|
||||
} else {
|
||||
queryCdmaRoamingMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetCdmaRoamingModeToDefault() {
|
||||
//set the mButtonCdmaRoam
|
||||
setValue(Integer.toString(TelephonyManager.CDMA_ROAMING_MODE_ANY));
|
||||
//set the Settings.System
|
||||
Settings.Global.putInt(
|
||||
getContext().getContentResolver(),
|
||||
Settings.Global.CDMA_ROAMING_MODE,
|
||||
TelephonyManager.CDMA_ROAMING_MODE_ANY);
|
||||
//Set the Status
|
||||
setCdmaRoamingMode(TelephonyManager.CDMA_ROAMING_MODE_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
private void queryCdmaRoamingMode() {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_ROAMING_PREFERENCE);
|
||||
msg.arg1 = mTelephonyManager.getCdmaRoamingMode();
|
||||
msg.sendToTarget();
|
||||
});
|
||||
}
|
||||
|
||||
private void setCdmaRoamingMode(int mode) {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_SET_ROAMING_PREFERENCE);
|
||||
msg.obj = mTelephonyManager.setCdmaRoamingMode(mode);
|
||||
msg.sendToTarget();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,35 @@ public final class CellInfoUtil {
|
||||
return oi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because
|
||||
* operatorInfo does not contain technology type while CellInfo is an abstract object that
|
||||
* requires to specify technology type. It doesn't matter which CellInfo type to use here, since
|
||||
* we only want to wrap the operator info and PLMN to a CellInfo object.
|
||||
*/
|
||||
public static CellInfo convertOperatorInfoToCellInfo(OperatorInfo operatorInfo) {
|
||||
String operatorNumeric = operatorInfo.getOperatorNumeric();
|
||||
String mcc = null;
|
||||
String mnc = null;
|
||||
if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) {
|
||||
mcc = operatorNumeric.substring(0, 3);
|
||||
mnc = operatorNumeric.substring(3);
|
||||
}
|
||||
CellIdentityGsm cig = new CellIdentityGsm(
|
||||
Integer.MAX_VALUE /* lac */,
|
||||
Integer.MAX_VALUE /* cid */,
|
||||
Integer.MAX_VALUE /* arfcn */,
|
||||
Integer.MAX_VALUE /* bsic */,
|
||||
mcc,
|
||||
mnc,
|
||||
operatorInfo.getOperatorAlphaLong(),
|
||||
operatorInfo.getOperatorAlphaShort());
|
||||
|
||||
CellInfoGsm ci = new CellInfoGsm();
|
||||
ci.setCellIdentity(cig);
|
||||
return ci;
|
||||
}
|
||||
|
||||
/** Checks whether the network operator is forbidden. */
|
||||
public static boolean isForbidden(CellInfo cellInfo, List<String> forbiddenPlmns) {
|
||||
String plmn = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric();
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.NetworkTemplate;
|
||||
import androidx.preference.Preference;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.net.DataUsageController;
|
||||
|
||||
/**
|
||||
* The preference that shows mobile data usage summary and
|
||||
* leads to mobile data usage list page.
|
||||
*/
|
||||
public class DataUsagePreference extends Preference {
|
||||
|
||||
private NetworkTemplate mTemplate;
|
||||
private int mSubId;
|
||||
|
||||
public DataUsagePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* After creating this preference, this functions needs to be called to
|
||||
* initialize which subID it connects to.
|
||||
*/
|
||||
public void initialize(int subId) {
|
||||
final Context context = getContext();
|
||||
mSubId = subId;
|
||||
mTemplate = getNetworkTemplate(context, subId);
|
||||
|
||||
DataUsageController controller = new DataUsageController(context);
|
||||
|
||||
DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate);
|
||||
setSummary(context.getString(R.string.data_usage_template,
|
||||
Formatter.formatFileSize(context, usageInfo.usageLevel), usageInfo.period));
|
||||
setIntent(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent intent = new Intent(Settings.ACTION_MOBILE_DATA_USAGE);
|
||||
|
||||
intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mTemplate);
|
||||
intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
private NetworkTemplate getNetworkTemplate(Context context, int subId) {
|
||||
TelephonyManager tm = (TelephonyManager) context
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
|
||||
tm.getSubscriberId(subId));
|
||||
return NetworkTemplate.normalize(mobileAll,
|
||||
tm.getMergedSubscriberIds());
|
||||
}
|
||||
}
|
||||
180
src/com/android/settings/mobilenetwork/GsmUmtsOptions.java
Normal file
180
src/com/android/settings/mobilenetwork/GsmUmtsOptions.java
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.mobilenetwork;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.PersistableBundle;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.telephony.Phone;
|
||||
import com.android.internal.telephony.PhoneConstants;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
/**
|
||||
* List of Network-specific settings screens.
|
||||
*/
|
||||
public class GsmUmtsOptions {
|
||||
private static final String LOG_TAG = "GsmUmtsOptions";
|
||||
|
||||
private CarrierConfigManager mCarrierConfigManager;
|
||||
private RestrictedPreference mButtonAPNExpand;
|
||||
private Preference mCategoryAPNExpand;
|
||||
Preference mCarrierSettingPref;
|
||||
|
||||
private NetworkOperators mNetworkOperator;
|
||||
|
||||
private static final String BUTTON_APN_EXPAND_KEY = "button_gsm_apn_key";
|
||||
private static final String CATEGORY_APN_EXPAND_KEY = "category_gsm_apn_key";
|
||||
private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key";
|
||||
|
||||
public static final String EXTRA_SUB_ID = "sub_id";
|
||||
private PreferenceFragmentCompat mPrefFragment;
|
||||
private PreferenceScreen mPrefScreen;
|
||||
|
||||
public GsmUmtsOptions(PreferenceFragmentCompat prefFragment, PreferenceScreen prefScreen,
|
||||
final int subId) {
|
||||
final Context context = prefFragment.getContext();
|
||||
mPrefFragment = prefFragment;
|
||||
mPrefScreen = prefScreen;
|
||||
mCarrierConfigManager = new CarrierConfigManager(context);
|
||||
mPrefFragment.addPreferencesFromResource(R.xml.gsm_umts_options);
|
||||
mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY);
|
||||
mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY);
|
||||
mNetworkOperator = (NetworkOperators) mPrefScreen
|
||||
.findPreference(NetworkOperators.CATEGORY_NETWORK_OPERATORS_KEY);
|
||||
mCarrierSettingPref = mPrefScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY);
|
||||
|
||||
mNetworkOperator.initialize();
|
||||
|
||||
update(subId);
|
||||
}
|
||||
|
||||
// Unlike mPrefFragment or mPrefScreen, subId may change during lifecycle of GsmUmtsOptions.
|
||||
// When that happens, we update GsmUmtsOptions with new parameters.
|
||||
protected void update(final int subId) {
|
||||
boolean addAPNExpand = true;
|
||||
boolean addNetworkOperatorsCategory = true;
|
||||
boolean addCarrierSettings = true;
|
||||
final TelephonyManager telephonyManager = TelephonyManager.from(mPrefFragment.getContext())
|
||||
.createForSubscriptionId(subId);
|
||||
//TODO(b/115429509): Get phone from subId
|
||||
Phone phone = null;
|
||||
if (phone == null) return;
|
||||
if (telephonyManager.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
|
||||
log("Not a GSM phone");
|
||||
addAPNExpand = false;
|
||||
mNetworkOperator.setEnabled(false);
|
||||
} else {
|
||||
log("Not a CDMA phone");
|
||||
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
|
||||
|
||||
// Determine which options to display. For GSM these are defaulted to true in
|
||||
// CarrierConfigManager, but they maybe overriden by DefaultCarrierConfigService or a
|
||||
// carrier app.
|
||||
// Note: these settings used to be controlled with overlays in
|
||||
// Telephony/res/values/config.xml
|
||||
if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_APN_EXPAND_BOOL)
|
||||
&& mCategoryAPNExpand != null) {
|
||||
addAPNExpand = false;
|
||||
}
|
||||
if (!carrierConfig.getBoolean(
|
||||
CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)) {
|
||||
addNetworkOperatorsCategory = false;
|
||||
}
|
||||
|
||||
if (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)) {
|
||||
if (phone.isCspPlmnEnabled()) {
|
||||
log("[CSP] Enabling Operator Selection menu.");
|
||||
mNetworkOperator.setEnabled(true);
|
||||
} else {
|
||||
log("[CSP] Disabling Operator Selection menu.");
|
||||
addNetworkOperatorsCategory = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read platform settings for carrier settings
|
||||
addCarrierSettings = carrierConfig.getBoolean(
|
||||
CarrierConfigManager.KEY_CARRIER_SETTINGS_ENABLE_BOOL);
|
||||
}
|
||||
|
||||
// Making no assumptions of whether they are added or removed at this point.
|
||||
// Calling add or remove explicitly to make sure they are updated.
|
||||
|
||||
if (addAPNExpand) {
|
||||
log("update: addAPNExpand");
|
||||
mButtonAPNExpand.setDisabledByAdmin(
|
||||
MobileNetworkUtils.isDpcApnEnforced(mButtonAPNExpand.getContext())
|
||||
? RestrictedLockUtilsInternal.getDeviceOwner(
|
||||
mButtonAPNExpand.getContext())
|
||||
: null);
|
||||
mButtonAPNExpand.setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
MetricsLogger.action(mButtonAPNExpand.getContext(),
|
||||
MetricsEvent.ACTION_MOBILE_NETWORK_APN_SETTINGS);
|
||||
// We need to build the Intent by hand as the Preference Framework
|
||||
// does not allow to add an Intent with some extras into a Preference
|
||||
// XML file
|
||||
final Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);
|
||||
// This will setup the Home and Search affordance
|
||||
intent.putExtra(":settings:show_fragment_as_subsetting", true);
|
||||
intent.putExtra(EXTRA_SUB_ID, subId);
|
||||
mPrefFragment.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mPrefScreen.addPreference(mCategoryAPNExpand);
|
||||
} else {
|
||||
mPrefScreen.removePreference(mCategoryAPNExpand);
|
||||
}
|
||||
|
||||
if (addNetworkOperatorsCategory) {
|
||||
mPrefScreen.addPreference(mNetworkOperator);
|
||||
mNetworkOperator.update(subId);
|
||||
} else {
|
||||
mPrefScreen.removePreference(mNetworkOperator);
|
||||
}
|
||||
|
||||
if (addCarrierSettings) {
|
||||
mPrefScreen.addPreference(mCarrierSettingPref);
|
||||
} else {
|
||||
mPrefScreen.removePreference(mCarrierSettingPref);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean preferenceTreeClick(Preference preference) {
|
||||
return mNetworkOperator.preferenceTreeClick(preference);
|
||||
}
|
||||
|
||||
protected void log(String s) {
|
||||
android.util.Log.d(LOG_TAG, s);
|
||||
}
|
||||
}
|
||||
|
||||
324
src/com/android/settings/mobilenetwork/MobileDataPreference.java
Normal file
324
src/com/android/settings/mobilenetwork/MobileDataPreference.java
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.Settings.Global;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Checkable;
|
||||
|
||||
import androidx.preference.DialogPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Customized Preference to enable / disable mobile data.
|
||||
* Basically copy of with com.android.settings.CellDataPreference.
|
||||
*/
|
||||
public class MobileDataPreference extends DialogPreference implements
|
||||
DialogInterface.OnClickListener {
|
||||
|
||||
private static final boolean DBG = false;
|
||||
private static final String TAG = "MobileDataPreference";
|
||||
|
||||
public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
public boolean mChecked;
|
||||
// Whether to show the dialog to ask switching default data subscription.
|
||||
// Should be true only when a multi-sim phone only supports data connection on a single phone,
|
||||
// and user is enabling data on the non-default phone.
|
||||
public boolean mMultiSimDialog;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
|
||||
public MobileDataPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);
|
||||
}
|
||||
|
||||
// Must be called to avoid binder leakage.
|
||||
void dispose() {
|
||||
mListener.setListener(false, mSubId, getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable s) {
|
||||
CellDataState state = (CellDataState) s;
|
||||
super.onRestoreInstanceState(state.getSuperState());
|
||||
mTelephonyManager = TelephonyManager.from(getContext());
|
||||
mChecked = state.mChecked;
|
||||
mMultiSimDialog = state.mMultiSimDialog;
|
||||
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
mSubId = state.mSubId;
|
||||
setKey(getKey() + mSubId);
|
||||
}
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
CellDataState state = new CellDataState(super.onSaveInstanceState());
|
||||
state.mChecked = mChecked;
|
||||
state.mMultiSimDialog = mMultiSimDialog;
|
||||
state.mSubId = mSubId;
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttached() {
|
||||
super.onAttached();
|
||||
mListener.setListener(true, mSubId, getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareForRemoval() {
|
||||
mListener.setListener(false, mSubId, getContext());
|
||||
super.onPrepareForRemoval();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this preference with subId.
|
||||
*/
|
||||
public void initialize(int subId) {
|
||||
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
throw new IllegalArgumentException("MobileDataPreference needs a SubscriptionInfo");
|
||||
}
|
||||
mSubscriptionManager = SubscriptionManager.from(getContext());
|
||||
mTelephonyManager = TelephonyManager.from(getContext());
|
||||
if (mSubId != subId) {
|
||||
mSubId = subId;
|
||||
setKey(getKey() + subId);
|
||||
}
|
||||
updateChecked();
|
||||
}
|
||||
|
||||
private void updateChecked() {
|
||||
setChecked(mTelephonyManager.getDataEnabled(mSubId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performClick() {
|
||||
if (!isEnabled() || !SubscriptionManager.isValidSubscriptionId(mSubId)) {
|
||||
return;
|
||||
}
|
||||
final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
|
||||
mSubId);
|
||||
final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
|
||||
final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
|
||||
final boolean isMultipleDataOnCapable =
|
||||
(mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
|
||||
final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
|
||||
&& currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
|
||||
if (mChecked) {
|
||||
if (!isMultiSim) {
|
||||
// disabling data; show confirmation dialog which eventually
|
||||
// calls setMobileDataEnabled() once user confirms.
|
||||
mMultiSimDialog = false;
|
||||
super.performClick();
|
||||
} else {
|
||||
// Don't show any dialog.
|
||||
setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
|
||||
}
|
||||
} else {
|
||||
if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
|
||||
// enabling data and setting to default; show confirmation dialog which eventually
|
||||
// calls setMobileDataEnabled() once user confirms.
|
||||
mMultiSimDialog = true;
|
||||
super.performClick();
|
||||
} else {
|
||||
// Don't show any dialog.
|
||||
setMobileDataEnabled(true /* enabled */, false /* disableOtherSubscriptions */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) {
|
||||
if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
|
||||
|
||||
MetricsLogger.action(getContext(), MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE,
|
||||
enabled);
|
||||
|
||||
mTelephonyManager.setDataEnabled(mSubId, enabled);
|
||||
|
||||
if (disableOtherSubscriptions) {
|
||||
disableDataForOtherSubscriptions(mSubId);
|
||||
}
|
||||
|
||||
setChecked(enabled);
|
||||
}
|
||||
|
||||
private void setChecked(boolean checked) {
|
||||
if (mChecked == checked) return;
|
||||
mChecked = checked;
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
View checkableView = holder.findViewById(com.android.internal.R.id.switch_widget);
|
||||
checkableView.setClickable(false);
|
||||
((Checkable) checkableView).setChecked(mChecked);
|
||||
}
|
||||
|
||||
//TODO(b/114749736): move it to preference controller
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||
if (mMultiSimDialog) {
|
||||
showMultiSimDialog(builder);
|
||||
} else {
|
||||
showDisableDialog(builder);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDisableDialog(AlertDialog.Builder builder) {
|
||||
builder.setTitle(null)
|
||||
.setMessage(R.string.data_usage_disable_mobile)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
}
|
||||
|
||||
private void showMultiSimDialog(AlertDialog.Builder builder) {
|
||||
final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
|
||||
final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
|
||||
|
||||
final String previousName = (nextSir == null)
|
||||
? getContext().getResources().getString(R.string.sim_selection_required_pref)
|
||||
: nextSir.getDisplayName().toString();
|
||||
|
||||
builder.setTitle(R.string.sim_change_data_title);
|
||||
builder.setMessage(getContext().getString(R.string.sim_change_data_message,
|
||||
String.valueOf(currentSir != null ? currentSir.getDisplayName() : null),
|
||||
previousName));
|
||||
|
||||
builder.setPositiveButton(android.R.string.ok, this);
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
}
|
||||
|
||||
private void disableDataForOtherSubscriptions(int subId) {
|
||||
List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
|
||||
if (subInfoList != null) {
|
||||
for (SubscriptionInfo subInfo : subInfoList) {
|
||||
if (subInfo.getSubscriptionId() != subId) {
|
||||
mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which != DialogInterface.BUTTON_POSITIVE) {
|
||||
return;
|
||||
}
|
||||
if (mMultiSimDialog) {
|
||||
mSubscriptionManager.setDefaultDataSubId(mSubId);
|
||||
setMobileDataEnabled(true /* enabled */, true /* disableOtherSubscriptions */);
|
||||
} else {
|
||||
// TODO: extend to modify policy enabled flag.
|
||||
setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
|
||||
}
|
||||
}
|
||||
|
||||
private final DataStateListener mListener = new DataStateListener() {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateChecked();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener that listens mobile data state change.
|
||||
*/
|
||||
public abstract static class DataStateListener extends ContentObserver {
|
||||
public DataStateListener() {
|
||||
super(new Handler(Looper.getMainLooper()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set / Unset data state listening, specifying subId.
|
||||
*/
|
||||
public void setListener(boolean listening, int subId, Context context) {
|
||||
if (listening) {
|
||||
Uri uri = Global.getUriFor(Global.MOBILE_DATA);
|
||||
if (TelephonyManager.getDefault().getSimCount() != 1) {
|
||||
uri = Global.getUriFor(Global.MOBILE_DATA + subId);
|
||||
}
|
||||
context.getContentResolver().registerContentObserver(uri, false, this);
|
||||
} else {
|
||||
context.getContentResolver().unregisterContentObserver(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents state of mobile data state.
|
||||
* Used by onSaveInstanceState and onRestoreInstanceState.
|
||||
*/
|
||||
public static class CellDataState extends BaseSavedState {
|
||||
public int mSubId;
|
||||
public boolean mChecked;
|
||||
public boolean mMultiSimDialog;
|
||||
|
||||
public CellDataState(Parcelable base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
public CellDataState(Parcel source) {
|
||||
super(source);
|
||||
mChecked = source.readByte() != 0;
|
||||
mMultiSimDialog = source.readByte() != 0;
|
||||
mSubId = source.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
|
||||
@Override
|
||||
public CellDataState createFromParcel(Parcel source) {
|
||||
return new CellDataState(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataState[] newArray(int size) {
|
||||
return new CellDataState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
1895
src/com/android/settings/mobilenetwork/MobileNetworkFragment.java
Normal file
1895
src/com/android/settings/mobilenetwork/MobileNetworkFragment.java
Normal file
File diff suppressed because it is too large
Load Diff
173
src/com/android/settings/mobilenetwork/MobileNetworkUtils.java
Normal file
173
src/com/android/settings/mobilenetwork/MobileNetworkUtils.java
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Settings;
|
||||
import android.telecom.PhoneAccountHandle;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.telephony.ims.feature.ImsFeature;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.ims.ImsException;
|
||||
import com.android.ims.ImsManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MobileNetworkUtils {
|
||||
|
||||
private static final String TAG = "MobileNetworkUtils";
|
||||
|
||||
// CID of the device.
|
||||
private static final String KEY_CID = "ro.boot.cid";
|
||||
// CIDs of devices which should not show anything related to eSIM.
|
||||
private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
|
||||
// System Property which is used to decide whether the default eSIM UI will be shown,
|
||||
// the default value is false.
|
||||
private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
|
||||
"esim.enable_esim_system_ui_by_default";
|
||||
|
||||
/**
|
||||
* Returns if DPC APNs are enforced.
|
||||
*/
|
||||
public static boolean isDpcApnEnforced(Context context) {
|
||||
try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI,
|
||||
null, null, null, null)) {
|
||||
if (enforceCursor == null || enforceCursor.getCount() != 1) {
|
||||
return false;
|
||||
}
|
||||
enforceCursor.moveToFirst();
|
||||
return enforceCursor.getInt(0) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Wifi calling is enabled for at least one phone.
|
||||
*/
|
||||
public static boolean isWifiCallingEnabled(Context context) {
|
||||
int phoneCount = TelephonyManager.from(context).getPhoneCount();
|
||||
for (int i = 0; i < phoneCount; i++) {
|
||||
if (isWifiCallingEnabled(context, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Wifi calling is enabled for the specific phone with id {@code phoneId}.
|
||||
*/
|
||||
public static boolean isWifiCallingEnabled(Context context, int phoneId) {
|
||||
final PhoneAccountHandle simCallManager =
|
||||
TelecomManager.from(context).getSimCallManager();
|
||||
|
||||
boolean isWifiCallingEnabled;
|
||||
if (simCallManager != null) {
|
||||
//TODO(b/114749736): build intent to query wifi calling feature
|
||||
final Intent intent = null;
|
||||
PackageManager pm = context.getPackageManager();
|
||||
isWifiCallingEnabled = intent != null
|
||||
&& !pm.queryIntentActivities(intent, 0 /* flags */).isEmpty();
|
||||
} else {
|
||||
ImsManager imsMgr = ImsManager.getInstance(context, phoneId);
|
||||
isWifiCallingEnabled = imsMgr != null
|
||||
&& imsMgr.isWfcEnabledByPlatform()
|
||||
&& imsMgr.isWfcProvisionedOnDevice()
|
||||
&& isImsServiceStateReady(imsMgr);
|
||||
}
|
||||
|
||||
return isWifiCallingEnabled;
|
||||
}
|
||||
|
||||
public static boolean isImsServiceStateReady(ImsManager imsMgr) {
|
||||
boolean isImsServiceStateReady = false;
|
||||
|
||||
try {
|
||||
if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
|
||||
isImsServiceStateReady = true;
|
||||
}
|
||||
} catch (ImsException ex) {
|
||||
Log.e(TAG, "Exception when trying to get ImsServiceStatus: " + ex);
|
||||
}
|
||||
|
||||
Log.d(TAG, "isImsServiceStateReady=" + isImsServiceStateReady);
|
||||
return isImsServiceStateReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the entry point to eUICC settings.
|
||||
*
|
||||
* <p>We show the entry point on any device which supports eUICC as long as either the eUICC
|
||||
* was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
|
||||
* the user has enabled development mode.
|
||||
*/
|
||||
public static boolean showEuiccSettings(Context context) {
|
||||
EuiccManager euiccManager =
|
||||
(EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
|
||||
if (!euiccManager.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String currentCountry = tm.getNetworkCountryIso().toLowerCase();
|
||||
String supportedCountries =
|
||||
Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES);
|
||||
boolean inEsimSupportedCountries = false;
|
||||
if (TextUtils.isEmpty(currentCountry)) {
|
||||
inEsimSupportedCountries = true;
|
||||
} else if (!TextUtils.isEmpty(supportedCountries)) {
|
||||
List<String> supportedCountryList =
|
||||
Arrays.asList(TextUtils.split(supportedCountries.toLowerCase(), ","));
|
||||
if (supportedCountryList.contains(currentCountry)) {
|
||||
inEsimSupportedCountries = true;
|
||||
}
|
||||
}
|
||||
final boolean esimIgnoredDevice =
|
||||
Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
|
||||
.contains(SystemProperties.get(KEY_CID, null));
|
||||
final boolean enabledEsimUiByDefault =
|
||||
SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
|
||||
final boolean euiccProvisioned =
|
||||
Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
|
||||
final boolean inDeveloperMode =
|
||||
Settings.Global.getInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
|
||||
|
||||
return (inDeveloperMode || euiccProvisioned
|
||||
|| (!esimIgnoredDevice && enabledEsimUiByDefault && inEsimSupportedCountries));
|
||||
}
|
||||
|
||||
public static PersistableBundle getCarrierConfigBySubId(int mSubId) {
|
||||
//TODO(b/114749736): get carrier config from subId
|
||||
return new PersistableBundle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SettingsBaseActivity;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
public class MobileSettingsActivity extends SettingsBaseActivity {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String MOBILE_SETTINGS_TAG = "mobile_settings:";
|
||||
public static final String KEY_SUBSCRIPTION_ID = "key_subscription_id";
|
||||
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
@VisibleForTesting
|
||||
int mPrevSubscriptionId;
|
||||
@VisibleForTesting
|
||||
List<SubscriptionInfo> mSubscriptionInfos;
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
//TODO(b/114749736): update fragment by new intent, or at least make sure this page shows
|
||||
// current tab for sim card
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mSubscriptionManager = getSystemService(SubscriptionManager.class);
|
||||
mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
|
||||
mPrevSubscriptionId = CollectionUtils.isEmpty(mSubscriptionInfos)
|
||||
? SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
: mSubscriptionInfos.get(0).getSubscriptionId();
|
||||
|
||||
setContentView(R.layout.mobile_settings_container);
|
||||
|
||||
updateBottomNavigationView();
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
switchFragment(new MobileNetworkFragment(), mPrevSubscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateBottomNavigationView() {
|
||||
final BottomNavigationView navigation = findViewById(R.id.bottom_nav);
|
||||
|
||||
if (CollectionUtils.size(mSubscriptionInfos) <= 1) {
|
||||
navigation.setVisibility(View.GONE);
|
||||
} else {
|
||||
final Menu menu = navigation.getMenu();
|
||||
menu.clear();
|
||||
for (int i = 0, size = mSubscriptionInfos.size(); i < size; i++) {
|
||||
final SubscriptionInfo subscriptionInfo = mSubscriptionInfos.get(i);
|
||||
menu.add(0, subscriptionInfo.getSubscriptionId(), i,
|
||||
subscriptionInfo.getDisplayName());
|
||||
}
|
||||
navigation.setOnNavigationItemSelectedListener(item -> {
|
||||
switchFragment(new MobileNetworkFragment(), item.getItemId());
|
||||
mPrevSubscriptionId = item.getItemId();
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void switchFragment(Fragment fragment, int subscriptionId) {
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
|
||||
|
||||
final Fragment hideFragment = fragmentManager.findFragmentByTag(
|
||||
buildFragmentTag(mPrevSubscriptionId));
|
||||
if (hideFragment != null) {
|
||||
fragmentTransaction.hide(hideFragment);
|
||||
}
|
||||
|
||||
Fragment showFragment = fragmentManager.findFragmentByTag(buildFragmentTag(subscriptionId));
|
||||
if (showFragment == null) {
|
||||
fragment.setArguments(bundle);
|
||||
fragmentTransaction.add(R.id.main_content, fragment, buildFragmentTag(subscriptionId));
|
||||
} else {
|
||||
showFragment.setArguments(bundle);
|
||||
fragmentTransaction.show(showFragment);
|
||||
}
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
|
||||
private String buildFragmentTag(int subscriptionId) {
|
||||
return MOBILE_SETTINGS_TAG + subscriptionId;
|
||||
}
|
||||
}
|
||||
301
src/com/android/settings/mobilenetwork/NetworkOperators.java
Normal file
301
src/com/android/settings/mobilenetwork/NetworkOperators.java
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.telephony.ServiceState;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
/**
|
||||
* "Networks" settings UI for the Phone app.
|
||||
*/
|
||||
public class NetworkOperators extends PreferenceCategory
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String LOG_TAG = "NetworkOperators";
|
||||
private static final boolean DBG = true;
|
||||
|
||||
private static final int EVENT_AUTO_SELECT_DONE = 100;
|
||||
private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 200;
|
||||
|
||||
//String keys for preference lookup
|
||||
public static final String BUTTON_NETWORK_SELECT_KEY = "button_network_select_key";
|
||||
public static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key";
|
||||
public static final String BUTTON_CHOOSE_NETWORK_KEY = "button_choose_network_key";
|
||||
public static final String CATEGORY_NETWORK_OPERATORS_KEY = "network_operators_category_key";
|
||||
|
||||
//preference objects
|
||||
private NetworkSelectListPreference mNetworkSelect;
|
||||
private TwoStatePreference mAutoSelect;
|
||||
private Preference mChooseNetwork;
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
private int mSubId;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
|
||||
// There's two sets of Auto-Select UI in this class.
|
||||
// If {@code com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI} set as true
|
||||
// {@link mChooseNetwork} will be used, otherwise {@link mNetworkSelect} will be used.
|
||||
boolean mEnableNewManualSelectNetworkUI;
|
||||
|
||||
public NetworkOperators(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NetworkOperators(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize NetworkOperators instance.
|
||||
*/
|
||||
public void initialize() {
|
||||
mEnableNewManualSelectNetworkUI = getContext().getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI);
|
||||
mAutoSelect = (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY);
|
||||
mChooseNetwork = findPreference(BUTTON_CHOOSE_NETWORK_KEY);
|
||||
mNetworkSelect = (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY);
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
removePreference(mNetworkSelect);
|
||||
} else {
|
||||
removePreference(mChooseNetwork);
|
||||
}
|
||||
mProgressDialog = new ProgressDialog(getContext());
|
||||
mTelephonyManager = TelephonyManager.from(getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update NetworkOperators instance if like subId is updated.
|
||||
*
|
||||
* @param subId Corresponding subscription ID of this network.
|
||||
*/
|
||||
protected void update(final int subId) {
|
||||
mSubId = subId;
|
||||
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
|
||||
|
||||
if (mAutoSelect != null) {
|
||||
mAutoSelect.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
if (mChooseNetwork != null) {
|
||||
ServiceState ss = mTelephonyManager.getServiceState();
|
||||
if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
|
||||
mChooseNetwork.setSummary(mTelephonyManager.getNetworkOperatorName());
|
||||
} else {
|
||||
mChooseNetwork.setSummary(R.string.network_disconnected);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mNetworkSelect != null) {
|
||||
mNetworkSelect.initialize(mSubId, this, mProgressDialog);
|
||||
}
|
||||
}
|
||||
getNetworkSelectionMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented to support onPreferenceChangeListener to look for preference
|
||||
* changes specifically on auto select button.
|
||||
*
|
||||
* @param preference is the preference to be changed, should be auto select button.
|
||||
* @param newValue should be the value of whether autoSelect is checked.
|
||||
*/
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference == mAutoSelect) {
|
||||
boolean autoSelect = (Boolean) newValue;
|
||||
if (DBG) logd("onPreferenceChange autoSelect: " + String.valueOf(autoSelect));
|
||||
selectNetworkAutomatic(autoSelect);
|
||||
MetricsLogger.action(getContext(),
|
||||
MetricsEvent.ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE, autoSelect);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_AUTO_SELECT_DONE:
|
||||
mAutoSelect.setEnabled(true);
|
||||
dismissProgressBar();
|
||||
|
||||
boolean isSuccessed = (boolean) msg.obj;
|
||||
|
||||
if (isSuccessed) {
|
||||
if (DBG) logd("automatic network selection: succeeded!");
|
||||
displayNetworkSelectionSucceeded();
|
||||
} else {
|
||||
if (DBG) logd("automatic network selection: failed!");
|
||||
displayNetworkSelectionFailed();
|
||||
}
|
||||
|
||||
break;
|
||||
case EVENT_GET_NETWORK_SELECTION_MODE_DONE:
|
||||
int networkSelectionMode = msg.arg1;
|
||||
if (networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN) {
|
||||
if (DBG) logd("get network selection mode: failed!");
|
||||
} else {
|
||||
boolean autoSelect = networkSelectionMode
|
||||
== TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
|
||||
if (DBG) {
|
||||
logd("get network selection mode: "
|
||||
+ (autoSelect ? "auto" : "manual") + " selection");
|
||||
}
|
||||
if (mAutoSelect != null) {
|
||||
mAutoSelect.setChecked(autoSelect);
|
||||
}
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
if (mChooseNetwork != null) {
|
||||
mChooseNetwork.setEnabled(!autoSelect);
|
||||
}
|
||||
} else {
|
||||
if (mNetworkSelect != null) {
|
||||
mNetworkSelect.setEnabled(!autoSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Used by both mAutoSelect and mNetworkSelect buttons.
|
||||
protected void displayNetworkSelectionFailed() {
|
||||
Toast.makeText(getContext(), R.string.connect_later, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
// Used by both mAutoSelect and mNetworkSelect buttons.
|
||||
protected void displayNetworkSelectionSucceeded() {
|
||||
Toast.makeText(getContext(), R.string.registration_done, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void selectNetworkAutomatic(boolean autoSelect) {
|
||||
if (DBG) logd("selectNetworkAutomatic: " + String.valueOf(autoSelect));
|
||||
|
||||
if (autoSelect) {
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
if (mChooseNetwork != null) {
|
||||
mChooseNetwork.setEnabled(!autoSelect);
|
||||
}
|
||||
} else {
|
||||
if (mNetworkSelect != null) {
|
||||
mNetworkSelect.setEnabled(!autoSelect);
|
||||
}
|
||||
}
|
||||
if (DBG) logd("select network automatically...");
|
||||
showAutoSelectProgressBar();
|
||||
mAutoSelect.setEnabled(false);
|
||||
if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
mTelephonyManager.setNetworkSelectionModeAutomatic();
|
||||
// Because TelephonyManager#setNetworkSelectionModeAutomatic doesn't have a
|
||||
// return value, we query the current network selection mode to tell if the
|
||||
// TelephonyManager#setNetworkSelectionModeAutomatic is successed.
|
||||
int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode();
|
||||
Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE);
|
||||
msg.obj = networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
|
||||
msg.sendToTarget();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
if (mChooseNetwork != null) {
|
||||
// Open the choose Network page automatically when user turn off the auto-select
|
||||
openChooseNetworkPage();
|
||||
}
|
||||
} else {
|
||||
if (mNetworkSelect != null) {
|
||||
mNetworkSelect.onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void getNetworkSelectionMode() {
|
||||
if (DBG) logd("getting network selection mode...");
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode();
|
||||
Message msg = mHandler.obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE);
|
||||
msg.arg1 = networkSelectionMode;
|
||||
msg.sendToTarget();
|
||||
});
|
||||
}
|
||||
|
||||
private void dismissProgressBar() {
|
||||
if (mProgressDialog != null && mProgressDialog.isShowing()) {
|
||||
mProgressDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void showAutoSelectProgressBar() {
|
||||
mProgressDialog.setMessage(
|
||||
getContext().getResources().getString(R.string.register_automatically));
|
||||
mProgressDialog.setCanceledOnTouchOutside(false);
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the Choose network page via {@alink NetworkSelectSettingActivity}
|
||||
*/
|
||||
public void openChooseNetworkPage() {
|
||||
//TODO(b/114749736): Build intent without calling static method
|
||||
Intent intent = new Intent();
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
|
||||
protected boolean preferenceTreeClick(Preference preference) {
|
||||
if (mEnableNewManualSelectNetworkUI) {
|
||||
if (DBG) logd("enable New AutoSelectNetwork UI");
|
||||
if (preference == mChooseNetwork) {
|
||||
openChooseNetworkPage();
|
||||
}
|
||||
return (preference == mAutoSelect || preference == mChooseNetwork);
|
||||
} else {
|
||||
return (preference == mAutoSelect || preference == mNetworkSelect);
|
||||
}
|
||||
}
|
||||
|
||||
private void logd(String msg) {
|
||||
Log.d(LOG_TAG, "[NetworksList] " + msg);
|
||||
}
|
||||
|
||||
private void loge(String msg) {
|
||||
Log.e(LOG_TAG, "[NetworksList] " + msg);
|
||||
}
|
||||
}
|
||||
286
src/com/android/settings/mobilenetwork/NetworkScanHelper.java
Normal file
286
src/com/android/settings/mobilenetwork/NetworkScanHelper.java
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.telephony.AccessNetworkConstants.AccessNetworkType;
|
||||
import android.telephony.CellInfo;
|
||||
import android.telephony.NetworkScan;
|
||||
import android.telephony.NetworkScanRequest;
|
||||
import android.telephony.RadioAccessSpecifier;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.TelephonyScanManager;
|
||||
import android.util.Log;
|
||||
|
||||
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.SettableFuture;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A helper class that builds the common interface and performs the network scan for two different
|
||||
* network scan APIs.
|
||||
*/
|
||||
public class NetworkScanHelper {
|
||||
public static final String TAG = "NetworkScanHelper";
|
||||
private static final boolean DBG = true;
|
||||
|
||||
/**
|
||||
* Callbacks interface to inform the network scan results.
|
||||
*/
|
||||
public interface NetworkScanCallback {
|
||||
/**
|
||||
* Called when the results is returned from {@link TelephonyManager}. This method will be
|
||||
* called at least one time if there is no error occurred during the network scan.
|
||||
*
|
||||
* <p> This method can be called multiple times in one network scan, until
|
||||
* {@link #onComplete()} or {@link #onError(int)} is called.
|
||||
*
|
||||
* @param results
|
||||
*/
|
||||
void onResults(List<CellInfo> results);
|
||||
|
||||
/**
|
||||
* Called when the current network scan process is finished. No more
|
||||
* {@link #onResults(List)} will be called for the current network scan after this method is
|
||||
* called.
|
||||
*/
|
||||
void onComplete();
|
||||
|
||||
/**
|
||||
* Called when an error occurred during the network scan process.
|
||||
*
|
||||
* <p> There is no more result returned from {@link TelephonyManager} if an error occurred.
|
||||
*
|
||||
* <p> {@link #onComplete()} will not be called if an error occurred.
|
||||
*
|
||||
* @see {@link NetworkScan.ScanErrorCode}
|
||||
*/
|
||||
void onError(int errorCode);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS})
|
||||
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
|
||||
* scan is completed. The complete results should be returned in the last called of
|
||||
* {@link NetworkScanCallback#onResults(List)}.
|
||||
*
|
||||
* <p> This is recommended to be used if modem supports the new network scan api
|
||||
* {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
|
||||
*/
|
||||
public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2;
|
||||
|
||||
/** The constants below are used in the async network scan. */
|
||||
private static final boolean INCREMENTAL_RESULTS = true;
|
||||
private static final int SEARCH_PERIODICITY_SEC = 5;
|
||||
private static final int MAX_SEARCH_TIME_SEC = 300;
|
||||
private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
|
||||
|
||||
private static final NetworkScanRequest NETWORK_SCAN_REQUEST =
|
||||
new NetworkScanRequest(
|
||||
NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
|
||||
new RadioAccessSpecifier[]{
|
||||
// GSM
|
||||
new RadioAccessSpecifier(
|
||||
AccessNetworkType.GERAN,
|
||||
null /* bands */,
|
||||
null /* channels */),
|
||||
// LTE
|
||||
new RadioAccessSpecifier(
|
||||
AccessNetworkType.EUTRAN,
|
||||
null /* bands */,
|
||||
null /* channels */),
|
||||
// WCDMA
|
||||
new RadioAccessSpecifier(
|
||||
AccessNetworkType.UTRAN,
|
||||
null /* bands */,
|
||||
null /* channels */)
|
||||
},
|
||||
SEARCH_PERIODICITY_SEC,
|
||||
MAX_SEARCH_TIME_SEC,
|
||||
INCREMENTAL_RESULTS,
|
||||
INCREMENTAL_RESULTS_PERIODICITY_SEC,
|
||||
null /* List of PLMN ids (MCC-MNC) */);
|
||||
|
||||
private final NetworkScanCallback mNetworkScanCallback;
|
||||
private final TelephonyManager mTelephonyManager;
|
||||
private final TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback;
|
||||
private final Executor mExecutor;
|
||||
|
||||
private NetworkScan mNetworkScanRequester;
|
||||
|
||||
/** Callbacks for sync network scan */
|
||||
private ListenableFuture<List<CellInfo>> mNetworkScanFuture;
|
||||
|
||||
public NetworkScanHelper(TelephonyManager tm, NetworkScanCallback callback, Executor executor) {
|
||||
mTelephonyManager = tm;
|
||||
mNetworkScanCallback = callback;
|
||||
mInternalNetworkScanCallback = new NetworkScanCallbackImpl();
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a network scan for the given type {@code type}.
|
||||
* {@link #NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS} is recommended if modem supports
|
||||
* {@link TelephonyManager#requestNetworkScan(
|
||||
* NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}.
|
||||
*
|
||||
* @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) {
|
||||
int errCode = Integer.parseInt(t.getMessage());
|
||||
onError(errCode);
|
||||
}
|
||||
});
|
||||
mExecutor.execute(new NetworkScanSyncTask(
|
||||
mTelephonyManager, (SettableFuture) mNetworkScanFuture));
|
||||
} else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) {
|
||||
if (DBG) Log.d(TAG, "start network scan async");
|
||||
mNetworkScanRequester = mTelephonyManager.requestNetworkScan(
|
||||
NETWORK_SCAN_REQUEST,
|
||||
mExecutor,
|
||||
mInternalNetworkScanCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The network scan of type {@link #NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS} can't be stopped,
|
||||
* however, the result of the current network scan won't be returned to the callback after
|
||||
* calling this method.
|
||||
*/
|
||||
public void stopNetworkQuery() {
|
||||
if (mNetworkScanRequester != null) {
|
||||
mNetworkScanRequester.stopScan();
|
||||
mNetworkScanFuture = null;
|
||||
}
|
||||
|
||||
if (mNetworkScanFuture != null) {
|
||||
mNetworkScanFuture.cancel(true /* mayInterruptIfRunning */);
|
||||
mNetworkScanFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void onResults(List<CellInfo> cellInfos) {
|
||||
mNetworkScanCallback.onResults(cellInfos);
|
||||
}
|
||||
|
||||
private void onComplete() {
|
||||
mNetworkScanCallback.onComplete();
|
||||
}
|
||||
|
||||
private void onError(int errCode) {
|
||||
mNetworkScanCallback.onError(errCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (DBG) Log.d(TAG, "async scan onResults() results = " + results);
|
||||
NetworkScanHelper.this.onResults(results);
|
||||
}
|
||||
|
||||
public void onComplete() {
|
||||
if (DBG) Log.d(TAG, "async scan onComplete()");
|
||||
NetworkScanHelper.this.onComplete();
|
||||
}
|
||||
|
||||
public void onError(@NetworkScan.ScanErrorCode int errCode) {
|
||||
if (DBG) Log.d(TAG, "async scan onError() errorCode = " + errCode);
|
||||
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() {
|
||||
if (DBG) Log.d(TAG, "sync scan start");
|
||||
CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks();
|
||||
if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) {
|
||||
List<CellInfo> cellInfos = result.getOperators()
|
||||
.stream()
|
||||
.map(operatorInfo
|
||||
-> CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo))
|
||||
.collect(Collectors.toList());
|
||||
if (DBG) Log.d(TAG, "sync scan complete");
|
||||
mCallback.set(cellInfos);
|
||||
} else {
|
||||
mCallback.setException(new Throwable(
|
||||
Integer.toString(convertToScanErrorCode(result.getStatus()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.mobilenetwork;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.telephony.CellInfo;
|
||||
import android.telephony.CellInfoCdma;
|
||||
import android.telephony.CellInfoGsm;
|
||||
import android.telephony.CellInfoLte;
|
||||
import android.telephony.CellInfoWcdma;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.BidiFormatter;
|
||||
import android.text.TextDirectionHeuristics;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.telephony.OperatorInfo;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
|
||||
/**
|
||||
* "Networks" preference in "Mobile network" settings UI for the Phone app.
|
||||
* It's used to manually search and choose mobile network. Enabled only when
|
||||
* autoSelect preference is turned off.
|
||||
*/
|
||||
public class NetworkSelectListPreference extends ListPreference
|
||||
implements DialogInterface.OnCancelListener,
|
||||
Preference.OnPreferenceChangeListener{
|
||||
|
||||
private static final String LOG_TAG = "networkSelect";
|
||||
private static final boolean DBG = true;
|
||||
|
||||
private static final int EVENT_MANUALLY_NETWORK_SELECTION_DONE = 1;
|
||||
private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
|
||||
private static final int EVENT_NETWORK_SCAN_COMPLETED = 3;
|
||||
private static final int EVENT_NETWORK_SCAN_ERROR = 4;
|
||||
|
||||
//dialog ids
|
||||
private static final int DIALOG_NETWORK_SELECTION = 100;
|
||||
private static final int DIALOG_NETWORK_LIST_LOAD = 200;
|
||||
|
||||
private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1);
|
||||
|
||||
private List<CellInfo> mCellInfoList;
|
||||
private CellInfo mCellInfo;
|
||||
|
||||
private int mSubId;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private NetworkScanHelper mNetworkScanHelper;
|
||||
private NetworkOperators mNetworkOperators;
|
||||
private List<String> mForbiddenPlmns;
|
||||
|
||||
private ProgressDialog mProgressDialog;
|
||||
public NetworkSelectListPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
showProgressDialog(DIALOG_NETWORK_LIST_LOAD);
|
||||
TelephonyManager telephonyManager = (TelephonyManager)
|
||||
getContext().getSystemService(Context.TELEPHONY_SERVICE);
|
||||
new AsyncTask<Void, Void, List<String>>() {
|
||||
@Override
|
||||
protected List<String> doInBackground(Void... voids) {
|
||||
String[] forbiddenPlmns = telephonyManager.getForbiddenPlmns();
|
||||
return forbiddenPlmns != null ? Arrays.asList(forbiddenPlmns) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<String> result) {
|
||||
mForbiddenPlmns = result;
|
||||
loadNetworksList();
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_MANUALLY_NETWORK_SELECTION_DONE:
|
||||
if (DBG) logd("hideProgressPanel");
|
||||
dismissProgressDialog();
|
||||
|
||||
boolean isSuccessed = (boolean) msg.obj;
|
||||
if (isSuccessed) {
|
||||
if (DBG) {
|
||||
logd("manual network selection: succeeded! "
|
||||
+ getNetworkTitle(mCellInfo));
|
||||
}
|
||||
mNetworkOperators.displayNetworkSelectionSucceeded();
|
||||
} else {
|
||||
if (DBG) logd("manual network selection: failed!");
|
||||
mNetworkOperators.displayNetworkSelectionFailed();
|
||||
}
|
||||
mNetworkOperators.getNetworkSelectionMode();
|
||||
break;
|
||||
|
||||
case EVENT_NETWORK_SCAN_RESULTS:
|
||||
List<CellInfo> results = (List<CellInfo>) msg.obj;
|
||||
results.removeIf(cellInfo -> cellInfo == null);
|
||||
mCellInfoList = new ArrayList<>(results);
|
||||
if (DBG) logd("CALLBACK_SCAN_RESULTS" + mCellInfoList.toString());
|
||||
break;
|
||||
|
||||
case EVENT_NETWORK_SCAN_COMPLETED:
|
||||
if (DBG) logd("scan complete, load the cellInfosList");
|
||||
dismissProgressDialog();
|
||||
networksListLoaded();
|
||||
break;
|
||||
case EVENT_NETWORK_SCAN_ERROR:
|
||||
dismissProgressDialog();
|
||||
displayNetworkQueryFailed();
|
||||
mNetworkOperators.getNetworkSelectionMode();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
private final NetworkScanHelper.NetworkScanCallback mCallback =
|
||||
new NetworkScanHelper.NetworkScanCallback() {
|
||||
public void onResults(List<CellInfo> results) {
|
||||
if (DBG) logd("get scan results: " + results.toString());
|
||||
Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
public void onComplete() {
|
||||
if (DBG) logd("network scan completed.");
|
||||
Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
public void onError(int error) {
|
||||
if (DBG) logd("network scan error.");
|
||||
Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
//implemented for DialogInterface.OnCancelListener
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
if (DBG) logd("user manually close the dialog");
|
||||
mNetworkScanHelper.stopNetworkQuery();
|
||||
|
||||
// If cancelled, we query NetworkSelectMode and update states of AutoSelect button.
|
||||
mNetworkOperators.getNetworkSelectionMode();
|
||||
}
|
||||
|
||||
//TODO(b/114749736): move this logic to preference controller
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
// If dismissed, we query NetworkSelectMode and update states of AutoSelect button.
|
||||
if (!positiveResult) {
|
||||
mNetworkOperators.getNetworkSelectionMode();
|
||||
}
|
||||
}
|
||||
|
||||
// This initialize method needs to be called for this preference to work properly.
|
||||
protected void initialize(int subId, NetworkOperators networkOperators,
|
||||
ProgressDialog progressDialog) {
|
||||
mSubId = subId;
|
||||
mNetworkOperators = networkOperators;
|
||||
// This preference should share the same progressDialog with networkOperators category.
|
||||
mProgressDialog = progressDialog;
|
||||
|
||||
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
|
||||
mNetworkScanHelper = new NetworkScanHelper(
|
||||
mTelephonyManager, mCallback, mNetworkScanExecutor);
|
||||
|
||||
setSummary(mTelephonyManager.getNetworkOperatorName());
|
||||
|
||||
setOnPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareForRemoval() {
|
||||
destroy();
|
||||
super.onPrepareForRemoval();
|
||||
}
|
||||
|
||||
private void destroy() {
|
||||
dismissProgressDialog();
|
||||
|
||||
if (mNetworkScanHelper != null) {
|
||||
mNetworkScanHelper.stopNetworkQuery();
|
||||
}
|
||||
|
||||
mNetworkScanExecutor.shutdown();
|
||||
}
|
||||
|
||||
private void displayEmptyNetworkList() {
|
||||
Toast.makeText(getContext(), R.string.empty_networks_list, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void displayNetworkQueryFailed() {
|
||||
Toast.makeText(getContext(), R.string.network_query_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void loadNetworksList() {
|
||||
if (DBG) logd("load networks list...");
|
||||
mNetworkScanHelper.startNetworkScan(
|
||||
NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS);
|
||||
}
|
||||
|
||||
private void networksListLoaded() {
|
||||
if (DBG) logd("networks list loaded");
|
||||
|
||||
mNetworkOperators.getNetworkSelectionMode();
|
||||
if (mCellInfoList != null) {
|
||||
// create a preference for each item in the list.
|
||||
// just use the operator name instead of the mildly
|
||||
// confusing mcc/mnc.
|
||||
List<CharSequence> networkEntriesList = new ArrayList<>();
|
||||
List<CharSequence> networkEntryValuesList = new ArrayList<>();
|
||||
for (CellInfo cellInfo: mCellInfoList) {
|
||||
// Display each operator name only once.
|
||||
String networkTitle = getNetworkTitle(cellInfo);
|
||||
if (CellInfoUtil.isForbidden(cellInfo, mForbiddenPlmns)) {
|
||||
networkTitle += " "
|
||||
+ getContext().getResources().getString(R.string.forbidden_network);
|
||||
}
|
||||
networkEntriesList.add(networkTitle);
|
||||
networkEntryValuesList.add(getOperatorNumeric(cellInfo));
|
||||
}
|
||||
setEntries(networkEntriesList.toArray(new CharSequence[networkEntriesList.size()]));
|
||||
setEntryValues(networkEntryValuesList.toArray(
|
||||
new CharSequence[networkEntryValuesList.size()]));
|
||||
|
||||
super.onClick();
|
||||
} else {
|
||||
displayEmptyNetworkList();
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissProgressDialog() {
|
||||
if (mProgressDialog != null && mProgressDialog.isShowing()) {
|
||||
try {
|
||||
mProgressDialog.dismiss();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
loge("Can't close the progress dialog " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showProgressDialog(int id) {
|
||||
if (mProgressDialog == null) {
|
||||
mProgressDialog = new ProgressDialog(getContext());
|
||||
} else {
|
||||
// Dismiss progress bar if it's showing now.
|
||||
dismissProgressDialog();
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case DIALOG_NETWORK_SELECTION:
|
||||
final String networkSelectMsg = getContext().getResources()
|
||||
.getString(R.string.register_on_network,
|
||||
getNetworkTitle(mCellInfo));
|
||||
mProgressDialog.setMessage(networkSelectMsg);
|
||||
mProgressDialog.setCanceledOnTouchOutside(false);
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
break;
|
||||
case DIALOG_NETWORK_LIST_LOAD:
|
||||
mProgressDialog.setMessage(
|
||||
getContext().getResources().getString(R.string.load_networks_progress));
|
||||
mProgressDialog.setCanceledOnTouchOutside(false);
|
||||
mProgressDialog.setCancelable(true);
|
||||
mProgressDialog.setIndeterminate(false);
|
||||
mProgressDialog.setOnCancelListener(this);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented to support onPreferenceChangeListener to look for preference
|
||||
* changes specifically on this button.
|
||||
*
|
||||
* @param preference is the preference to be changed, should be network select button.
|
||||
* @param newValue should be the value of the selection as index of operators.
|
||||
*/
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int operatorIndex = findIndexOfValue((String) newValue);
|
||||
mCellInfo = mCellInfoList.get(operatorIndex);
|
||||
if (DBG) logd("selected network: " + mCellInfo.toString());
|
||||
|
||||
MetricsLogger.action(getContext(),
|
||||
MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK);
|
||||
|
||||
if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
final OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo);
|
||||
if (DBG) logd("manually selected network: " + operatorInfo.toString());
|
||||
boolean isSuccessed = mTelephonyManager.setNetworkSelectionModeManual(
|
||||
operatorInfo, true /* persistSelection */);
|
||||
Message msg = mHandler.obtainMessage(EVENT_MANUALLY_NETWORK_SELECTION_DONE);
|
||||
msg.obj = isSuccessed;
|
||||
msg.sendToTarget();
|
||||
});
|
||||
} else {
|
||||
loge("Error selecting network, subscription Id is invalid " + mSubId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the network obtained in the manual search.
|
||||
*
|
||||
* @param cellInfo contains the information of the network.
|
||||
* @return Long Name if not null/empty, otherwise Short Name if not null/empty,
|
||||
* else MCCMNC string.
|
||||
*/
|
||||
private String getNetworkTitle(CellInfo cellInfo) {
|
||||
OperatorInfo ni = getOperatorInfoFromCellInfo(cellInfo);
|
||||
|
||||
if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) {
|
||||
return ni.getOperatorAlphaLong();
|
||||
} else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) {
|
||||
return ni.getOperatorAlphaShort();
|
||||
} else {
|
||||
BidiFormatter bidiFormatter = BidiFormatter.getInstance();
|
||||
return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator numeric (MCCMNC) obtained in the manual search.
|
||||
*
|
||||
* @param cellInfo contains the information of the network.
|
||||
* @return MCCMNC string.
|
||||
*/
|
||||
private String getOperatorNumeric(CellInfo cellInfo) {
|
||||
return getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a cell info into an operator info.
|
||||
*/
|
||||
private OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) {
|
||||
OperatorInfo oi;
|
||||
if (cellInfo instanceof CellInfoLte) {
|
||||
CellInfoLte lte = (CellInfoLte) cellInfo;
|
||||
oi = new OperatorInfo(
|
||||
(String) lte.getCellIdentity().getOperatorAlphaLong(),
|
||||
(String) lte.getCellIdentity().getOperatorAlphaShort(),
|
||||
lte.getCellIdentity().getMobileNetworkOperator());
|
||||
} else if (cellInfo instanceof CellInfoWcdma) {
|
||||
CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo;
|
||||
oi = new OperatorInfo(
|
||||
(String) wcdma.getCellIdentity().getOperatorAlphaLong(),
|
||||
(String) wcdma.getCellIdentity().getOperatorAlphaShort(),
|
||||
wcdma.getCellIdentity().getMobileNetworkOperator());
|
||||
} else if (cellInfo instanceof CellInfoGsm) {
|
||||
CellInfoGsm gsm = (CellInfoGsm) cellInfo;
|
||||
oi = new OperatorInfo(
|
||||
(String) gsm.getCellIdentity().getOperatorAlphaLong(),
|
||||
(String) gsm.getCellIdentity().getOperatorAlphaShort(),
|
||||
gsm.getCellIdentity().getMobileNetworkOperator());
|
||||
} else if (cellInfo instanceof CellInfoCdma) {
|
||||
CellInfoCdma cdma = (CellInfoCdma) cellInfo;
|
||||
oi = new OperatorInfo(
|
||||
(String) cdma.getCellIdentity().getOperatorAlphaLong(),
|
||||
(String) cdma.getCellIdentity().getOperatorAlphaShort(),
|
||||
"" /* operator numeric */);
|
||||
} else {
|
||||
oi = new OperatorInfo("", "", "");
|
||||
}
|
||||
return oi;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable superState = super.onSaveInstanceState();
|
||||
if (isPersistent()) {
|
||||
// No need to save instance state since it's persistent
|
||||
return superState;
|
||||
}
|
||||
|
||||
final SavedState myState = new SavedState(superState);
|
||||
myState.mDialogListEntries = getEntries();
|
||||
myState.mDialogListEntryValues = getEntryValues();
|
||||
myState.mCellInfoList = mCellInfoList;
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
if (state == null || !state.getClass().equals(SavedState.class)) {
|
||||
// Didn't save state for us in onSaveInstanceState
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
SavedState myState = (SavedState) state;
|
||||
|
||||
if (getEntries() == null && myState.mDialogListEntries != null) {
|
||||
setEntries(myState.mDialogListEntries);
|
||||
}
|
||||
if (getEntryValues() == null && myState.mDialogListEntryValues != null) {
|
||||
setEntryValues(myState.mDialogListEntryValues);
|
||||
}
|
||||
if (mCellInfoList == null && myState.mCellInfoList != null) {
|
||||
mCellInfoList = myState.mCellInfoList;
|
||||
}
|
||||
|
||||
super.onRestoreInstanceState(myState.getSuperState());
|
||||
}
|
||||
|
||||
/**
|
||||
* We save entries, entryValues and operatorInfoList into bundle.
|
||||
* At onCreate of fragment, dialog will be restored if it was open. In this case,
|
||||
* we need to restore entries, entryValues and operatorInfoList. Without those information,
|
||||
* onPreferenceChange will fail if user select network from the dialog.
|
||||
*/
|
||||
private static class SavedState extends BaseSavedState {
|
||||
CharSequence[] mDialogListEntries;
|
||||
CharSequence[] mDialogListEntryValues;
|
||||
List<CellInfo> mCellInfoList;
|
||||
|
||||
SavedState(Parcel source) {
|
||||
super(source);
|
||||
final ClassLoader boot = Object.class.getClassLoader();
|
||||
mDialogListEntries = source.readCharSequenceArray();
|
||||
mDialogListEntryValues = source.readCharSequenceArray();
|
||||
mCellInfoList = source.readParcelableList(mCellInfoList, boot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeCharSequenceArray(mDialogListEntries);
|
||||
dest.writeCharSequenceArray(mDialogListEntryValues);
|
||||
dest.writeParcelableList(mCellInfoList, flags);
|
||||
}
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void logd(String msg) {
|
||||
Log.d(LOG_TAG, "[NetworksList] " + msg);
|
||||
}
|
||||
|
||||
private void loge(String msg) {
|
||||
Log.e(LOG_TAG, "[NetworksList] " + msg);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@ import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.telephony.OperatorInfo;
|
||||
@@ -47,10 +51,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
/**
|
||||
* "Choose network" settings UI for the Phone app.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
|
||||
/**
|
||||
* A dialog fragment that asks the user if they are sure they want to turn on data roaming
|
||||
* to avoid accidental charges.
|
||||
*/
|
||||
public class RoamingDialogFragment extends InstrumentedDialogFragment implements OnClickListener {
|
||||
|
||||
public static final String SUB_ID_KEY = "sub_id_key";
|
||||
|
||||
private CarrierConfigManager mCarrierConfigManager;
|
||||
private int mSubId;
|
||||
|
||||
/**
|
||||
* The interface we expect a host activity to implement.
|
||||
*/
|
||||
public interface RoamingDialogListener {
|
||||
void onPositiveButtonClick(DialogFragment dialog);
|
||||
}
|
||||
|
||||
// the host activity which implements the listening interface
|
||||
private RoamingDialogListener mListener;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
Bundle args = getArguments();
|
||||
mSubId = args.getInt(SUB_ID_KEY);
|
||||
mCarrierConfigManager = new CarrierConfigManager(context);
|
||||
|
||||
//TODO(b/114749736): set target fragment in host fragment
|
||||
Fragment fragment = getTargetFragment();
|
||||
try {
|
||||
mListener = (RoamingDialogListener) fragment;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(fragment.toString() +
|
||||
"must implement RoamingDialogListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
int title = R.string.roaming_alert_title;
|
||||
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
|
||||
if (carrierConfig != null && carrierConfig.getBoolean(
|
||||
CarrierConfigManager.KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL)) {
|
||||
title = R.string.roaming_check_price_warning;
|
||||
}
|
||||
builder.setMessage(getResources().getString(R.string.roaming_warning))
|
||||
.setTitle(title)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, this)
|
||||
.setNegativeButton(android.R.string.no, this);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
//TODO(b/114749736): add category for roaming dialog
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// let the host know that the positive button has been clicked
|
||||
if (which == dialog.BUTTON_POSITIVE) {
|
||||
mListener.onPositiveButtonClick(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.mobilenetwork.MobileSettingsActivity;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
import com.android.settingslib.Utils;
|
||||
@@ -146,7 +147,8 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (KEY_MOBILE_NETWORK_SETTINGS.equals(preference.getKey())) {
|
||||
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.MOBILE_NETWORK_V2)) {
|
||||
//TODO(b/110260193): go to the mobile network page existed in settings
|
||||
final Intent intent = new Intent(mContext, MobileSettingsActivity.class);
|
||||
mContext.startActivity(intent);
|
||||
} else {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.search.DeviceIndexFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
import com.android.settings.slices.SlicesFeatureProvider;
|
||||
@@ -106,8 +105,6 @@ public abstract class FeatureFactory {
|
||||
|
||||
public abstract AccountFeatureProvider getAccountFeatureProvider();
|
||||
|
||||
public abstract DeviceIndexFeatureProvider getDeviceIndexFeatureProvider();
|
||||
|
||||
public static final class FactoryNotFoundException extends RuntimeException {
|
||||
public FactoryNotFoundException(Throwable throwable) {
|
||||
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
|
||||
|
||||
@@ -41,8 +41,6 @@ import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
|
||||
import com.android.settings.search.DeviceIndexFeatureProvider;
|
||||
import com.android.settings.search.DeviceIndexFeatureProviderImpl;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProviderImpl;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
@@ -73,7 +71,6 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
private UserFeatureProvider mUserFeatureProvider;
|
||||
private SlicesFeatureProvider mSlicesFeatureProvider;
|
||||
private AccountFeatureProvider mAccountFeatureProvider;
|
||||
private DeviceIndexFeatureProviderImpl mDeviceIndexFeatureProvider;
|
||||
|
||||
@Override
|
||||
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
|
||||
@@ -211,12 +208,4 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
}
|
||||
return mAccountFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceIndexFeatureProvider getDeviceIndexFeatureProvider() {
|
||||
if (mDeviceIndexFeatureProvider == null) {
|
||||
mDeviceIndexFeatureProvider = new DeviceIndexFeatureProviderImpl();
|
||||
}
|
||||
return mDeviceIndexFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +435,14 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
@VisibleForTesting
|
||||
void updatePreferencesOrFinish(boolean isRecreatingActivity) {
|
||||
Intent intent = getActivity().getIntent();
|
||||
int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
|
||||
int quality = -1;
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
|
||||
} else {
|
||||
// For non-file encrypted devices we need to show encryption interstitial, so always
|
||||
// show the lock type picker and ignore PASSWORD_TYPE_KEY.
|
||||
Log.i(TAG, "Ignoring PASSWORD_TYPE_KEY because device is not file encrypted");
|
||||
}
|
||||
if (quality == -1) {
|
||||
// If caller didn't specify password quality, show UI and allow the user to choose.
|
||||
quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import android.os.storage.StorageManager;
|
||||
|
||||
/**
|
||||
* Wrapper class to allow Robolectric to shadow methods introduced in newer API
|
||||
*/
|
||||
public class StorageManagerWrapper {
|
||||
|
||||
public static boolean isFileEncryptedNativeOrEmulated() {
|
||||
return StorageManager.isFileEncryptedNativeOrEmulated();
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.search;
|
||||
|
||||
import static com.android.settings.slices.SliceDeepLinkSpringBoard.INTENT;
|
||||
import static com.android.settings.slices.SliceDeepLinkSpringBoard.SETTINGS;
|
||||
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.slices.SettingsSliceProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public interface DeviceIndexFeatureProvider {
|
||||
|
||||
String TAG = "DeviceIndex";
|
||||
|
||||
String INDEX_VERSION = "settings:index_version";
|
||||
String INDEX_LANGUAGE = "settings:language";
|
||||
|
||||
// Increment when new items are added to ensure they get pushed to the device index.
|
||||
String VERSION = Build.FINGERPRINT;
|
||||
|
||||
// When the device language changes, re-index so Slices trigger in device language.
|
||||
Locale LANGUAGE = Locale.getDefault();
|
||||
|
||||
default boolean isIndexingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri,
|
||||
List<String> keywords);
|
||||
|
||||
void clearIndex(Context context);
|
||||
|
||||
default void updateIndex(Context context, boolean force) {
|
||||
if (!isIndexingEnabled()) {
|
||||
Log.i(TAG, "Skipping: device index is not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils.isDeviceProvisioned(context)) {
|
||||
Log.w(TAG, "Skipping: device is not provisioned");
|
||||
return;
|
||||
}
|
||||
|
||||
final ComponentName jobComponent = new ComponentName(context.getPackageName(),
|
||||
DeviceIndexUpdateJobService.class.getName());
|
||||
|
||||
try {
|
||||
final int callerUid = Binder.getCallingUid();
|
||||
final ServiceInfo si = context.getPackageManager().getServiceInfo(jobComponent,
|
||||
PackageManager.MATCH_DIRECT_BOOT_AWARE
|
||||
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
|
||||
if (si == null) {
|
||||
Log.w(TAG, "Skipping: No such service " + jobComponent);
|
||||
return;
|
||||
}
|
||||
if (si.applicationInfo.uid != callerUid) {
|
||||
Log.w(TAG, "Skipping: Uid cannot schedule DeviceIndexUpdate: " + callerUid);
|
||||
return;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "Skipping: error finding DeviceIndexUpdateJobService from packageManager");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force && skipIndex(context)) {
|
||||
Log.i(TAG, "Skipping: already indexed.");
|
||||
// No need to update.
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent scheduling multiple jobs
|
||||
setIndexState(context);
|
||||
|
||||
final int jobId = context.getResources().getInteger(R.integer.device_index_update);
|
||||
// Schedule a job so that we know it'll be able to complete, but try to run as
|
||||
// soon as possible.
|
||||
context.getSystemService(JobScheduler.class).schedule(
|
||||
new JobInfo.Builder(jobId, jobComponent)
|
||||
.setPersisted(false)
|
||||
.setMinimumLatency(1000)
|
||||
.setOverrideDeadline(1)
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
static Uri createDeepLink(String s) {
|
||||
return new Uri.Builder().scheme(SETTINGS)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.appendQueryParameter(INTENT, s)
|
||||
.build();
|
||||
}
|
||||
|
||||
static boolean skipIndex(Context context) {
|
||||
final boolean isSameVersion = TextUtils.equals(
|
||||
Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION);
|
||||
final boolean isSameLanguage = TextUtils.equals(
|
||||
Settings.Secure.getString(context.getContentResolver(), INDEX_LANGUAGE),
|
||||
LANGUAGE.toString());
|
||||
return isSameLanguage && isSameVersion;
|
||||
}
|
||||
|
||||
static void setIndexState(Context context) {
|
||||
Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION);
|
||||
Settings.Secure.putString(context.getContentResolver(), INDEX_LANGUAGE,
|
||||
LANGUAGE.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DeviceIndexFeatureProviderImpl implements DeviceIndexFeatureProvider {
|
||||
|
||||
@Override
|
||||
public void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri,
|
||||
List<String> keywords) {
|
||||
// Not enabled by default.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearIndex(Context context) {
|
||||
// Not enabled by default.
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.search;
|
||||
|
||||
import static android.app.slice.Slice.HINT_LARGE;
|
||||
import static android.app.slice.Slice.HINT_TITLE;
|
||||
import static android.app.slice.SliceItem.FORMAT_TEXT;
|
||||
|
||||
import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.net.Uri.Builder;
|
||||
import android.provider.SettingsSlicesContract;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceItem;
|
||||
import androidx.slice.SliceMetadata;
|
||||
import androidx.slice.SliceViewManager;
|
||||
import androidx.slice.SliceViewManager.SliceCallback;
|
||||
import androidx.slice.core.SliceQuery;
|
||||
import androidx.slice.widget.ListContent;
|
||||
import androidx.slice.widget.SliceContent;
|
||||
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.slices.SettingsSliceProvider;
|
||||
import com.android.settings.slices.SliceDeepLinkSpringBoard;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class DeviceIndexUpdateJobService extends JobService {
|
||||
|
||||
private static final String TAG = "DeviceIndexUpdate";
|
||||
private static final boolean DEBUG = false;
|
||||
@VisibleForTesting
|
||||
protected boolean mRunningJob;
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
if (DEBUG) Log.d(TAG, "onStartJob");
|
||||
if (!mRunningJob) {
|
||||
mRunningJob = true;
|
||||
Thread thread = new Thread(() -> updateIndex(params));
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
thread.start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
if (DEBUG) Log.d(TAG, "onStopJob " + mRunningJob);
|
||||
if (mRunningJob) {
|
||||
mRunningJob = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void updateIndex(JobParameters params) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Starting index");
|
||||
}
|
||||
final DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(this)
|
||||
.getDeviceIndexFeatureProvider();
|
||||
final SliceViewManager manager = getSliceViewManager();
|
||||
final Uri baseUri = new Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.build();
|
||||
final Uri platformBaseUri = new Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSlicesContract.AUTHORITY)
|
||||
.build();
|
||||
final Collection<Uri> slices = manager.getSliceDescendants(baseUri);
|
||||
slices.addAll(manager.getSliceDescendants(platformBaseUri));
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Indexing " + slices.size() + " slices");
|
||||
}
|
||||
|
||||
indexProvider.clearIndex(this /* context */);
|
||||
|
||||
for (Uri slice : slices) {
|
||||
if (!mRunningJob) {
|
||||
return;
|
||||
}
|
||||
Slice loadedSlice = bindSliceSynchronous(manager, slice);
|
||||
// TODO: Get Title APIs on SliceMetadata and use that.
|
||||
SliceMetadata metaData = getMetadata(loadedSlice);
|
||||
CharSequence title = findTitle(loadedSlice, metaData);
|
||||
if (title != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice);
|
||||
}
|
||||
indexProvider.index(this, title, slice, createDeepLink(
|
||||
new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE)
|
||||
.setPackage(getPackageName())
|
||||
.putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE, slice.toString())
|
||||
.toUri(Intent.URI_ANDROID_APP_SCHEME)),
|
||||
metaData.getSliceKeywords());
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Done indexing");
|
||||
}
|
||||
jobFinished(params, false);
|
||||
}
|
||||
|
||||
protected SliceViewManager getSliceViewManager() {
|
||||
return SliceViewManager.getInstance(this);
|
||||
}
|
||||
|
||||
protected SliceMetadata getMetadata(Slice loadedSlice) {
|
||||
return SliceMetadata.from(this, loadedSlice);
|
||||
}
|
||||
|
||||
protected CharSequence findTitle(Slice loadedSlice, SliceMetadata metaData) {
|
||||
ListContent content = new ListContent(null, loadedSlice);
|
||||
SliceContent headerItem = content.getHeader();
|
||||
if (headerItem == null) {
|
||||
if (content.getRowItems().size() != 0) {
|
||||
headerItem = content.getRowItems().get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Look for a title, then large text, then any text at all.
|
||||
SliceItem title = SliceQuery.find(headerItem.getSliceItem(), FORMAT_TEXT, HINT_TITLE, null);
|
||||
if (title != null) {
|
||||
return title.getText();
|
||||
}
|
||||
title = SliceQuery.find(headerItem.getSliceItem(), FORMAT_TEXT, HINT_LARGE, null);
|
||||
if (title != null) {
|
||||
return title.getText();
|
||||
}
|
||||
title = SliceQuery.find(headerItem.getSliceItem(), FORMAT_TEXT);
|
||||
if (title != null) {
|
||||
return title.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Slice bindSliceSynchronous(SliceViewManager manager, Uri slice) {
|
||||
final Slice[] returnSlice = new Slice[1];
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
SliceCallback callback = new SliceCallback() {
|
||||
@Override
|
||||
public void onSliceUpdated(Slice s) {
|
||||
try {
|
||||
SliceMetadata m = SliceMetadata.from(DeviceIndexUpdateJobService.this, s);
|
||||
if (m.getLoadingState() == SliceMetadata.LOADED_ALL) {
|
||||
returnSlice[0] = s;
|
||||
latch.countDown();
|
||||
manager.unregisterSliceCallback(slice, this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, slice + " cannot be indexed", e);
|
||||
returnSlice[0] = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Register a callback until we get a loaded slice.
|
||||
manager.registerSliceCallback(slice, callback);
|
||||
// Trigger the first bind in case no loading is needed.
|
||||
callback.onSliceUpdated(manager.bindSlice(slice));
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return returnSlice[0];
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.search.SearchIndexableResources;
|
||||
|
||||
@@ -63,10 +64,12 @@ public interface SearchFeatureProvider {
|
||||
view.setOnClickListener(tb -> {
|
||||
final Intent intent = SEARCH_UI_INTENT;
|
||||
intent.setPackage(getSettingsIntelligencePkgName());
|
||||
final Context context = activity.getApplicationContext();
|
||||
|
||||
FeatureFactory.getFactory(
|
||||
activity.getApplicationContext()).getSlicesFeatureProvider()
|
||||
FeatureFactory.getFactory(context).getSlicesFeatureProvider()
|
||||
.indexSliceDataAsync(activity.getApplicationContext());
|
||||
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
|
||||
.action(context, MetricsProto.MetricsEvent.ACTION_SEARCH_RESULTS);
|
||||
activity.startActivityForResult(intent, REQUEST_CODE);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.search.actionbar;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
@@ -25,6 +26,7 @@ import android.view.MenuItem;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
@@ -70,10 +72,12 @@ public class SearchMenuController implements LifecycleObserver, OnCreateOptionsM
|
||||
searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
|
||||
searchItem.setOnMenuItemClickListener(target -> {
|
||||
final Context context = mHost.getContext();
|
||||
final Intent intent = SearchFeatureProvider.SEARCH_UI_INTENT;
|
||||
intent.setPackage(FeatureFactory.getFactory(mHost.getContext())
|
||||
.getSearchFeatureProvider().getSettingsIntelligencePkgName());
|
||||
|
||||
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
|
||||
.action(context, MetricsProto.MetricsEvent.ACTION_SEARCH_RESULTS);
|
||||
mHost.startActivityForResult(intent, 0 /* requestCode */);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.android.settings.bluetooth.BluetoothSliceBuilder;
|
||||
import com.android.settings.location.LocationSliceBuilder;
|
||||
import com.android.settings.notification.ZenModeSliceBuilder;
|
||||
@@ -90,6 +92,14 @@ public class SliceDeepLinkSpringBoard extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
public static Uri createDeepLink(String s) {
|
||||
return new Uri.Builder().scheme(SETTINGS)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.appendQueryParameter(INTENT, s)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Intent parse(Uri uri, String pkg) throws URISyntaxException {
|
||||
Intent intent = Intent.parseUri(uri.getQueryParameter(INTENT),
|
||||
Intent.URI_ANDROID_APP_SCHEME);
|
||||
|
||||
@@ -1,606 +0,0 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import static android.net.TrafficStats.MB_IN_BYTES;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.NetworkPolicy;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.net.TrafficStats;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.text.format.Formatter.BytesResult;
|
||||
import android.text.format.Time;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.widget.ChartSweepView.OnSweepListener;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Specific {@link ChartView} that displays {@link ChartNetworkSeriesView} along
|
||||
* with {@link ChartSweepView} for inspection ranges and warning/limits.
|
||||
*/
|
||||
public class ChartDataUsageView extends ChartView {
|
||||
|
||||
private static final int MSG_UPDATE_AXIS = 100;
|
||||
private static final long DELAY_MILLIS = 250;
|
||||
|
||||
private ChartGridView mGrid;
|
||||
private ChartNetworkSeriesView mSeries;
|
||||
private ChartNetworkSeriesView mDetailSeries;
|
||||
|
||||
private NetworkStatsHistory mHistory;
|
||||
|
||||
private ChartSweepView mSweepWarning;
|
||||
private ChartSweepView mSweepLimit;
|
||||
|
||||
private long mInspectStart;
|
||||
private long mInspectEnd;
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
/** Current maximum value of {@link #mVert}. */
|
||||
private long mVertMax;
|
||||
|
||||
public interface DataUsageChartListener {
|
||||
public void onWarningChanged();
|
||||
public void onLimitChanged();
|
||||
public void requestWarningEdit();
|
||||
public void requestLimitEdit();
|
||||
}
|
||||
|
||||
private DataUsageChartListener mListener;
|
||||
|
||||
public ChartDataUsageView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public ChartDataUsageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ChartDataUsageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(new TimeAxis(), new InvertedChartAxis(new DataAxis()));
|
||||
|
||||
mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
final ChartSweepView sweep = (ChartSweepView) msg.obj;
|
||||
updateVertAxisBounds(sweep);
|
||||
updateEstimateVisible();
|
||||
|
||||
// we keep dispatching repeating updates until sweep is dropped
|
||||
sendUpdateAxisDelayed(sweep, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
mGrid = (ChartGridView) findViewById(R.id.grid);
|
||||
mSeries = (ChartNetworkSeriesView) findViewById(R.id.series);
|
||||
mDetailSeries = (ChartNetworkSeriesView) findViewById(R.id.detail_series);
|
||||
mDetailSeries.setVisibility(View.GONE);
|
||||
|
||||
mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit);
|
||||
mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning);
|
||||
|
||||
// prevent sweeps from crossing each other
|
||||
mSweepWarning.setValidRangeDynamic(null, mSweepLimit);
|
||||
mSweepLimit.setValidRangeDynamic(mSweepWarning, null);
|
||||
|
||||
// mark neighbors for checking touch events against
|
||||
mSweepLimit.setNeighbors(mSweepWarning);
|
||||
mSweepWarning.setNeighbors(mSweepLimit);
|
||||
|
||||
mSweepWarning.addOnSweepListener(mVertListener);
|
||||
mSweepLimit.addOnSweepListener(mVertListener);
|
||||
|
||||
mSweepWarning.setDragInterval(5 * MB_IN_BYTES);
|
||||
mSweepLimit.setDragInterval(5 * MB_IN_BYTES);
|
||||
|
||||
// tell everyone about our axis
|
||||
mGrid.init(mHoriz, mVert);
|
||||
mSeries.init(mHoriz, mVert);
|
||||
mDetailSeries.init(mHoriz, mVert);
|
||||
mSweepWarning.init(mVert);
|
||||
mSweepLimit.init(mVert);
|
||||
|
||||
setActivated(false);
|
||||
}
|
||||
|
||||
public void setListener(DataUsageChartListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void bindNetworkStats(NetworkStatsHistory stats) {
|
||||
mSeries.bindNetworkStats(stats);
|
||||
mHistory = stats;
|
||||
updateVertAxisBounds(null);
|
||||
updateEstimateVisible();
|
||||
updatePrimaryRange();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void bindDetailNetworkStats(NetworkStatsHistory stats) {
|
||||
mDetailSeries.bindNetworkStats(stats);
|
||||
mDetailSeries.setVisibility(stats != null ? View.VISIBLE : View.GONE);
|
||||
if (mHistory != null) {
|
||||
mDetailSeries.setEndTime(mHistory.getEnd());
|
||||
}
|
||||
updateVertAxisBounds(null);
|
||||
updateEstimateVisible();
|
||||
updatePrimaryRange();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void bindNetworkPolicy(NetworkPolicy policy) {
|
||||
if (policy == null) {
|
||||
mSweepLimit.setVisibility(View.INVISIBLE);
|
||||
mSweepLimit.setValue(-1);
|
||||
mSweepWarning.setVisibility(View.INVISIBLE);
|
||||
mSweepWarning.setValue(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
|
||||
mSweepLimit.setVisibility(View.VISIBLE);
|
||||
mSweepLimit.setEnabled(true);
|
||||
mSweepLimit.setValue(policy.limitBytes);
|
||||
} else {
|
||||
mSweepLimit.setVisibility(View.INVISIBLE);
|
||||
mSweepLimit.setEnabled(false);
|
||||
mSweepLimit.setValue(-1);
|
||||
}
|
||||
|
||||
if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
|
||||
mSweepWarning.setVisibility(View.VISIBLE);
|
||||
mSweepWarning.setValue(policy.warningBytes);
|
||||
} else {
|
||||
mSweepWarning.setVisibility(View.INVISIBLE);
|
||||
mSweepWarning.setValue(-1);
|
||||
}
|
||||
|
||||
updateVertAxisBounds(null);
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update {@link #mVert} to both show data from {@link NetworkStatsHistory}
|
||||
* and controls from {@link NetworkPolicy}.
|
||||
*/
|
||||
private void updateVertAxisBounds(ChartSweepView activeSweep) {
|
||||
final long max = mVertMax;
|
||||
|
||||
long newMax = 0;
|
||||
if (activeSweep != null) {
|
||||
final int adjustAxis = activeSweep.shouldAdjustAxis();
|
||||
if (adjustAxis > 0) {
|
||||
// hovering around upper edge, grow axis
|
||||
newMax = max * 11 / 10;
|
||||
} else if (adjustAxis < 0) {
|
||||
// hovering around lower edge, shrink axis
|
||||
newMax = max * 9 / 10;
|
||||
} else {
|
||||
newMax = max;
|
||||
}
|
||||
}
|
||||
|
||||
// always show known data and policy lines
|
||||
final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue());
|
||||
final long maxSeries = Math.max(mSeries.getMaxVisible(), mDetailSeries.getMaxVisible());
|
||||
final long maxVisible = Math.max(maxSeries, maxSweep) * 12 / 10;
|
||||
final long maxDefault = Math.max(maxVisible, 50 * MB_IN_BYTES);
|
||||
newMax = Math.max(maxDefault, newMax);
|
||||
|
||||
// only invalidate when vertMax actually changed
|
||||
if (newMax != mVertMax) {
|
||||
mVertMax = newMax;
|
||||
|
||||
final boolean changed = mVert.setBounds(0L, newMax);
|
||||
mSweepWarning.setValidRange(0L, newMax);
|
||||
mSweepLimit.setValidRange(0L, newMax);
|
||||
|
||||
if (changed) {
|
||||
mSeries.invalidatePath();
|
||||
mDetailSeries.invalidatePath();
|
||||
}
|
||||
|
||||
mGrid.invalidate();
|
||||
|
||||
// since we just changed axis, make sweep recalculate its value
|
||||
if (activeSweep != null) {
|
||||
activeSweep.updateValueFromPosition();
|
||||
}
|
||||
|
||||
// layout other sweeps to match changed axis
|
||||
// TODO: find cleaner way of doing this, such as requesting full
|
||||
// layout and making activeSweep discard its tracking MotionEvent.
|
||||
if (mSweepLimit != activeSweep) {
|
||||
layoutSweep(mSweepLimit);
|
||||
}
|
||||
if (mSweepWarning != activeSweep) {
|
||||
layoutSweep(mSweepWarning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control {@link ChartNetworkSeriesView#setEstimateVisible(boolean)} based
|
||||
* on how close estimate comes to {@link #mSweepWarning}.
|
||||
*/
|
||||
private void updateEstimateVisible() {
|
||||
final long maxEstimate = mSeries.getMaxEstimate();
|
||||
|
||||
// show estimate when near warning/limit
|
||||
long interestLine = Long.MAX_VALUE;
|
||||
if (mSweepWarning.isEnabled()) {
|
||||
interestLine = mSweepWarning.getValue();
|
||||
} else if (mSweepLimit.isEnabled()) {
|
||||
interestLine = mSweepLimit.getValue();
|
||||
}
|
||||
|
||||
if (interestLine < 0) {
|
||||
interestLine = Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
final boolean estimateVisible = (maxEstimate >= interestLine * 7 / 10);
|
||||
mSeries.setEstimateVisible(estimateVisible);
|
||||
}
|
||||
|
||||
private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) {
|
||||
if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) {
|
||||
mHandler.sendMessageDelayed(
|
||||
mHandler.obtainMessage(MSG_UPDATE_AXIS, sweep), DELAY_MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearUpdateAxisDelayed(ChartSweepView sweep) {
|
||||
mHandler.removeMessages(MSG_UPDATE_AXIS, sweep);
|
||||
}
|
||||
|
||||
private OnSweepListener mVertListener = new OnSweepListener() {
|
||||
@Override
|
||||
public void onSweep(ChartSweepView sweep, boolean sweepDone) {
|
||||
if (sweepDone) {
|
||||
clearUpdateAxisDelayed(sweep);
|
||||
updateEstimateVisible();
|
||||
|
||||
if (sweep == mSweepWarning && mListener != null) {
|
||||
mListener.onWarningChanged();
|
||||
} else if (sweep == mSweepLimit && mListener != null) {
|
||||
mListener.onLimitChanged();
|
||||
}
|
||||
} else {
|
||||
// while moving, kick off delayed grow/shrink axis updates
|
||||
sendUpdateAxisDelayed(sweep, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestEdit(ChartSweepView sweep) {
|
||||
if (sweep == mSweepWarning && mListener != null) {
|
||||
mListener.requestWarningEdit();
|
||||
} else if (sweep == mSweepLimit && mListener != null) {
|
||||
mListener.requestLimitEdit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (isActivated()) return false;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
return true;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
setActivated(true);
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getInspectStart() {
|
||||
return mInspectStart;
|
||||
}
|
||||
|
||||
public long getInspectEnd() {
|
||||
return mInspectEnd;
|
||||
}
|
||||
|
||||
public long getWarningBytes() {
|
||||
return mSweepWarning.getLabelValue();
|
||||
}
|
||||
|
||||
public long getLimitBytes() {
|
||||
return mSweepLimit.getLabelValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exact time range that should be displayed, updating how
|
||||
* {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the
|
||||
* last "week" of available data, without triggering listener events.
|
||||
*/
|
||||
public void setVisibleRange(long visibleStart, long visibleEnd) {
|
||||
final boolean changed = mHoriz.setBounds(visibleStart, visibleEnd);
|
||||
mGrid.setBounds(visibleStart, visibleEnd);
|
||||
mSeries.setBounds(visibleStart, visibleEnd);
|
||||
mDetailSeries.setBounds(visibleStart, visibleEnd);
|
||||
|
||||
mInspectStart = visibleStart;
|
||||
mInspectEnd = visibleEnd;
|
||||
|
||||
requestLayout();
|
||||
if (changed) {
|
||||
mSeries.invalidatePath();
|
||||
mDetailSeries.invalidatePath();
|
||||
}
|
||||
|
||||
updateVertAxisBounds(null);
|
||||
updateEstimateVisible();
|
||||
updatePrimaryRange();
|
||||
}
|
||||
|
||||
private void updatePrimaryRange() {
|
||||
// prefer showing primary range on detail series, when available
|
||||
if (mDetailSeries.getVisibility() == View.VISIBLE) {
|
||||
mSeries.setSecondary(true);
|
||||
} else {
|
||||
mSeries.setSecondary(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeAxis implements ChartAxis {
|
||||
private static final int FIRST_DAY_OF_WEEK = Calendar.getInstance().getFirstDayOfWeek() - 1;
|
||||
|
||||
private long mMin;
|
||||
private long mMax;
|
||||
private float mSize;
|
||||
|
||||
public TimeAxis() {
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
setBounds(currentTime - DateUtils.DAY_IN_MILLIS * 30, currentTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mMin, mMax, mSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBounds(long min, long max) {
|
||||
if (mMin != min || mMax != max) {
|
||||
mMin = min;
|
||||
mMax = max;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSize(float size) {
|
||||
if (mSize != size) {
|
||||
mSize = size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float convertToPoint(long value) {
|
||||
return (mSize * (value - mMin)) / (mMax - mMin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToValue(float point) {
|
||||
return (long) (mMin + ((point * (mMax - mMin)) / mSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long buildLabel(Resources res, SpannableStringBuilder builder, long value) {
|
||||
// TODO: convert to better string
|
||||
builder.replace(0, builder.length(), Long.toString(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getTickPoints() {
|
||||
final float[] ticks = new float[32];
|
||||
int i = 0;
|
||||
|
||||
// tick mark for first day of each week
|
||||
final Time time = new Time();
|
||||
time.set(mMax);
|
||||
time.monthDay -= time.weekDay - FIRST_DAY_OF_WEEK;
|
||||
time.hour = time.minute = time.second = 0;
|
||||
|
||||
time.normalize(true);
|
||||
long timeMillis = time.toMillis(true);
|
||||
while (timeMillis > mMin) {
|
||||
if (timeMillis <= mMax) {
|
||||
ticks[i++] = convertToPoint(timeMillis);
|
||||
}
|
||||
time.monthDay -= 7;
|
||||
time.normalize(true);
|
||||
timeMillis = time.toMillis(true);
|
||||
}
|
||||
|
||||
return Arrays.copyOf(ticks, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int shouldAdjustAxis(long value) {
|
||||
// time axis never adjusts
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataAxis implements ChartAxis {
|
||||
private long mMin;
|
||||
private long mMax;
|
||||
private float mSize;
|
||||
|
||||
private static final boolean LOG_SCALE = false;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mMin, mMax, mSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBounds(long min, long max) {
|
||||
if (mMin != min || mMax != max) {
|
||||
mMin = min;
|
||||
mMax = max;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSize(float size) {
|
||||
if (mSize != size) {
|
||||
mSize = size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float convertToPoint(long value) {
|
||||
if (LOG_SCALE) {
|
||||
// derived polynomial fit to make lower values more visible
|
||||
final double normalized = ((double) value - mMin) / (mMax - mMin);
|
||||
final double fraction = Math.pow(10,
|
||||
0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624);
|
||||
return (float) (fraction * mSize);
|
||||
} else {
|
||||
return (mSize * (value - mMin)) / (mMax - mMin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToValue(float point) {
|
||||
if (LOG_SCALE) {
|
||||
final double normalized = point / mSize;
|
||||
final double fraction = 1.3102228476089056629
|
||||
* Math.pow(normalized, 2.7111774693164631640);
|
||||
return (long) (mMin + (fraction * (mMax - mMin)));
|
||||
} else {
|
||||
return (long) (mMin + ((point * (mMax - mMin)) / mSize));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object sSpanSize = new Object();
|
||||
private static final Object sSpanUnit = new Object();
|
||||
|
||||
@Override
|
||||
public long buildLabel(Resources res, SpannableStringBuilder builder, long value) {
|
||||
value = MathUtils.constrain(value, 0, TrafficStats.TB_IN_BYTES);
|
||||
final BytesResult result = Formatter.formatBytes(res, value,
|
||||
Formatter.FLAG_SHORTER | Formatter.FLAG_CALCULATE_ROUNDED);
|
||||
setText(builder, sSpanSize, result.value, "^1");
|
||||
setText(builder, sSpanUnit, result.units, "^2");
|
||||
return result.roundedBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getTickPoints() {
|
||||
final long range = mMax - mMin;
|
||||
|
||||
// target about 16 ticks on screen, rounded to nearest power of 2
|
||||
final long tickJump = roundUpToPowerOfTwo(range / 16);
|
||||
final int tickCount = (int) (range / tickJump);
|
||||
final float[] tickPoints = new float[tickCount];
|
||||
long value = mMin;
|
||||
for (int i = 0; i < tickPoints.length; i++) {
|
||||
tickPoints[i] = convertToPoint(value);
|
||||
value += tickJump;
|
||||
}
|
||||
|
||||
return tickPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int shouldAdjustAxis(long value) {
|
||||
final float point = convertToPoint(value);
|
||||
if (point < mSize * 0.1) {
|
||||
return -1;
|
||||
} else if (point > mSize * 0.85) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setText(
|
||||
SpannableStringBuilder builder, Object key, CharSequence text, String bootstrap) {
|
||||
int start = builder.getSpanStart(key);
|
||||
int end = builder.getSpanEnd(key);
|
||||
if (start == -1) {
|
||||
start = TextUtils.indexOf(builder, bootstrap);
|
||||
end = start + bootstrap.length();
|
||||
builder.setSpan(key, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
}
|
||||
builder.replace(start, end, text);
|
||||
}
|
||||
|
||||
private static long roundUpToPowerOfTwo(long i) {
|
||||
// NOTE: borrowed from Hashtable.roundUpToPowerOfTwo()
|
||||
|
||||
i--; // If input is a power of two, shift its high-order bit right
|
||||
|
||||
// "Smear" the high-order bit all the way to the right
|
||||
i |= i >>> 1;
|
||||
i |= i >>> 2;
|
||||
i |= i >>> 4;
|
||||
i |= i >>> 8;
|
||||
i |= i >>> 16;
|
||||
i |= i >>> 32;
|
||||
|
||||
i++;
|
||||
|
||||
return i > 0 ? i : Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* {@link NetworkStatsHistory} series to render inside a {@link ChartView},
|
||||
* using {@link ChartAxis} to map into screen coordinates.
|
||||
*/
|
||||
public class ChartNetworkSeriesView extends View {
|
||||
private static final String TAG = "ChartNetworkSeriesView";
|
||||
private static final boolean LOGD = false;
|
||||
|
||||
private static final boolean ESTIMATE_ENABLED = false;
|
||||
|
||||
private ChartAxis mHoriz;
|
||||
private ChartAxis mVert;
|
||||
|
||||
private Paint mPaintStroke;
|
||||
private Paint mPaintFill;
|
||||
private Paint mPaintFillSecondary;
|
||||
private Paint mPaintEstimate;
|
||||
|
||||
private NetworkStatsHistory mStats;
|
||||
|
||||
private Path mPathStroke;
|
||||
private Path mPathFill;
|
||||
private Path mPathEstimate;
|
||||
|
||||
private int mSafeRegion;
|
||||
|
||||
private long mStart;
|
||||
private long mEnd;
|
||||
|
||||
/** Series will be extended to reach this end time. */
|
||||
private long mEndTime = Long.MIN_VALUE;
|
||||
|
||||
private boolean mPathValid = false;
|
||||
private boolean mEstimateVisible = false;
|
||||
private boolean mSecondary = false;
|
||||
|
||||
private long mMax;
|
||||
private long mMaxEstimate;
|
||||
|
||||
public ChartNetworkSeriesView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public ChartNetworkSeriesView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.ChartNetworkSeriesView, defStyle, 0);
|
||||
|
||||
final int stroke = a.getColor(R.styleable.ChartNetworkSeriesView_strokeColor, Color.RED);
|
||||
final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED);
|
||||
final int fillSecondary = a.getColor(
|
||||
R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED);
|
||||
final int safeRegion = a.getDimensionPixelSize(
|
||||
R.styleable.ChartNetworkSeriesView_safeRegion, 0);
|
||||
|
||||
setChartColor(stroke, fill, fillSecondary);
|
||||
setSafeRegion(safeRegion);
|
||||
setWillNotDraw(false);
|
||||
|
||||
a.recycle();
|
||||
|
||||
mPathStroke = new Path();
|
||||
mPathFill = new Path();
|
||||
mPathEstimate = new Path();
|
||||
}
|
||||
|
||||
void init(ChartAxis horiz, ChartAxis vert) {
|
||||
mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
|
||||
mVert = Preconditions.checkNotNull(vert, "missing vert");
|
||||
}
|
||||
|
||||
public void setChartColor(int stroke, int fill, int fillSecondary) {
|
||||
mPaintStroke = new Paint();
|
||||
mPaintStroke.setStrokeWidth(4.0f * getResources().getDisplayMetrics().density);
|
||||
mPaintStroke.setColor(stroke);
|
||||
mPaintStroke.setStyle(Style.STROKE);
|
||||
mPaintStroke.setAntiAlias(true);
|
||||
|
||||
mPaintFill = new Paint();
|
||||
mPaintFill.setColor(fill);
|
||||
mPaintFill.setStyle(Style.FILL);
|
||||
mPaintFill.setAntiAlias(true);
|
||||
|
||||
mPaintFillSecondary = new Paint();
|
||||
mPaintFillSecondary.setColor(fillSecondary);
|
||||
mPaintFillSecondary.setStyle(Style.FILL);
|
||||
mPaintFillSecondary.setAntiAlias(true);
|
||||
|
||||
mPaintEstimate = new Paint();
|
||||
mPaintEstimate.setStrokeWidth(3.0f);
|
||||
mPaintEstimate.setColor(fillSecondary);
|
||||
mPaintEstimate.setStyle(Style.STROKE);
|
||||
mPaintEstimate.setAntiAlias(true);
|
||||
mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1));
|
||||
}
|
||||
|
||||
public void setSafeRegion(int safeRegion) {
|
||||
mSafeRegion = safeRegion;
|
||||
}
|
||||
|
||||
public void bindNetworkStats(NetworkStatsHistory stats) {
|
||||
mStats = stats;
|
||||
invalidatePath();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setBounds(long start, long end) {
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
}
|
||||
|
||||
public void setSecondary(boolean secondary) {
|
||||
mSecondary = secondary;
|
||||
}
|
||||
|
||||
public void invalidatePath() {
|
||||
mPathValid = false;
|
||||
mMax = 0;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase any existing {@link Path} and generate series outline based on
|
||||
* currently bound {@link NetworkStatsHistory} data.
|
||||
*/
|
||||
private void generatePath() {
|
||||
if (LOGD) Log.d(TAG, "generatePath()");
|
||||
|
||||
mMax = 0;
|
||||
mPathStroke.reset();
|
||||
mPathFill.reset();
|
||||
mPathEstimate.reset();
|
||||
mPathValid = true;
|
||||
|
||||
// bail when not enough stats to render
|
||||
if (mStats == null || mStats.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
|
||||
boolean started = false;
|
||||
float lastX = 0;
|
||||
float lastY = height;
|
||||
long lastTime = mHoriz.convertToValue(lastX);
|
||||
|
||||
// move into starting position
|
||||
mPathStroke.moveTo(lastX, lastY);
|
||||
mPathFill.moveTo(lastX, lastY);
|
||||
|
||||
// TODO: count fractional data from first bucket crossing start;
|
||||
// currently it only accepts first full bucket.
|
||||
|
||||
long totalData = 0;
|
||||
|
||||
NetworkStatsHistory.Entry entry = null;
|
||||
|
||||
final int start = mStats.getIndexBefore(mStart);
|
||||
final int end = mStats.getIndexAfter(mEnd);
|
||||
for (int i = start; i <= end; i++) {
|
||||
entry = mStats.getValues(i, entry);
|
||||
|
||||
final long startTime = entry.bucketStart;
|
||||
final long endTime = startTime + entry.bucketDuration;
|
||||
|
||||
final float startX = mHoriz.convertToPoint(startTime);
|
||||
final float endX = mHoriz.convertToPoint(endTime);
|
||||
|
||||
// skip until we find first stats on screen
|
||||
if (endX < 0) continue;
|
||||
|
||||
// increment by current bucket total
|
||||
totalData += entry.rxBytes + entry.txBytes;
|
||||
|
||||
final float startY = lastY;
|
||||
final float endY = mVert.convertToPoint(totalData);
|
||||
|
||||
if (lastTime != startTime) {
|
||||
// gap in buckets; line to start of current bucket
|
||||
mPathStroke.lineTo(startX, startY);
|
||||
mPathFill.lineTo(startX, startY);
|
||||
}
|
||||
|
||||
// always draw to end of current bucket
|
||||
mPathStroke.lineTo(endX, endY);
|
||||
mPathFill.lineTo(endX, endY);
|
||||
|
||||
lastX = endX;
|
||||
lastY = endY;
|
||||
lastTime = endTime;
|
||||
}
|
||||
|
||||
// when data falls short, extend to requested end time
|
||||
if (lastTime < mEndTime) {
|
||||
lastX = mHoriz.convertToPoint(mEndTime);
|
||||
|
||||
mPathStroke.lineTo(lastX, lastY);
|
||||
mPathFill.lineTo(lastX, lastY);
|
||||
}
|
||||
|
||||
if (LOGD) {
|
||||
final RectF bounds = new RectF();
|
||||
mPathFill.computeBounds(bounds, true);
|
||||
Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData="
|
||||
+ totalData);
|
||||
}
|
||||
|
||||
// drop to bottom of graph from current location
|
||||
mPathFill.lineTo(lastX, height);
|
||||
mPathFill.lineTo(0, height);
|
||||
|
||||
mMax = totalData;
|
||||
|
||||
if (ESTIMATE_ENABLED) {
|
||||
// build estimated data
|
||||
mPathEstimate.moveTo(lastX, lastY);
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
final long bucketDuration = mStats.getBucketDuration();
|
||||
|
||||
// long window is average over two weeks
|
||||
entry = mStats.getValues(lastTime - WEEK_IN_MILLIS * 2, lastTime, now, entry);
|
||||
final long longWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
|
||||
/ entry.bucketDuration;
|
||||
|
||||
long futureTime = 0;
|
||||
while (lastX < width) {
|
||||
futureTime += bucketDuration;
|
||||
|
||||
// short window is day average last week
|
||||
final long lastWeekTime = lastTime - WEEK_IN_MILLIS + (futureTime % WEEK_IN_MILLIS);
|
||||
entry = mStats.getValues(lastWeekTime - DAY_IN_MILLIS, lastWeekTime, now, entry);
|
||||
final long shortWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
|
||||
/ entry.bucketDuration;
|
||||
|
||||
totalData += (longWindow * 7 + shortWindow * 3) / 10;
|
||||
|
||||
lastX = mHoriz.convertToPoint(lastTime + futureTime);
|
||||
lastY = mVert.convertToPoint(totalData);
|
||||
|
||||
mPathEstimate.lineTo(lastX, lastY);
|
||||
}
|
||||
|
||||
mMaxEstimate = totalData;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setEndTime(long endTime) {
|
||||
mEndTime = endTime;
|
||||
}
|
||||
|
||||
public void setEstimateVisible(boolean estimateVisible) {
|
||||
mEstimateVisible = ESTIMATE_ENABLED ? estimateVisible : false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public long getMaxEstimate() {
|
||||
return mMaxEstimate;
|
||||
}
|
||||
|
||||
public long getMaxVisible() {
|
||||
final long maxVisible = mEstimateVisible ? mMaxEstimate : mMax;
|
||||
if (maxVisible <= 0 && mStats != null) {
|
||||
// haven't generated path yet; fall back to raw data
|
||||
final NetworkStatsHistory.Entry entry = mStats.getValues(mStart, mEnd, null);
|
||||
return entry.rxBytes + entry.txBytes;
|
||||
} else {
|
||||
return maxVisible;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int save;
|
||||
|
||||
if (!mPathValid) {
|
||||
generatePath();
|
||||
}
|
||||
|
||||
if (mEstimateVisible) {
|
||||
save = canvas.save();
|
||||
canvas.clipRect(0, 0, getWidth(), getHeight());
|
||||
canvas.drawPath(mPathEstimate, mPaintEstimate);
|
||||
canvas.restoreToCount(save);
|
||||
}
|
||||
|
||||
final Paint paintFill = mSecondary ? mPaintFillSecondary : mPaintFill;
|
||||
|
||||
save = canvas.save();
|
||||
canvas.clipRect(mSafeRegion, 0, getWidth(), getHeight() - mSafeRegion);
|
||||
canvas.drawPath(mPathFill, paintFill);
|
||||
canvas.restoreToCount(save);
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Container for two-dimensional chart, drawn with a combination of
|
||||
* {@link ChartGridView}, {@link ChartNetworkSeriesView} and {@link ChartSweepView}
|
||||
* children. The entire chart uses {@link ChartAxis} to map between raw values
|
||||
* {@link ChartGridView} and {@link ChartSweepView} children. The entire chart uses
|
||||
* {@link ChartAxis} to map between raw values
|
||||
* and screen coordinates.
|
||||
*/
|
||||
public class ChartView extends FrameLayout {
|
||||
@@ -112,13 +112,7 @@ public class ChartView extends FrameLayout {
|
||||
|
||||
parentRect.set(mContent);
|
||||
|
||||
if (child instanceof ChartNetworkSeriesView) {
|
||||
// series are always laid out to fill entire graph area
|
||||
// TODO: handle scrolling for series larger than content area
|
||||
Gravity.apply(params.gravity, width, height, parentRect, childRect);
|
||||
child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
|
||||
|
||||
} else if (child instanceof ChartGridView) {
|
||||
if (child instanceof ChartGridView) {
|
||||
// Grid uses some extra room for labels
|
||||
Gravity.apply(params.gravity, width, height, parentRect, childRect);
|
||||
child.layout(childRect.left, childRect.top, childRect.right,
|
||||
|
||||
@@ -30,15 +30,13 @@ import android.widget.Button;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.biometrics.BiometricEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroductionTest
|
||||
.ShadowStorageManagerWrapper;
|
||||
import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
import com.android.settings.password.StorageManagerWrapper;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowFingerprintManager;
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowStorageManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -51,8 +49,6 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
import org.robolectric.shadows.ShadowActivity.IntentForResult;
|
||||
import org.robolectric.shadows.ShadowKeyguardManager;
|
||||
@@ -61,7 +57,7 @@ import org.robolectric.shadows.ShadowKeyguardManager;
|
||||
@Config(shadows = {
|
||||
ShadowFingerprintManager.class,
|
||||
ShadowLockPatternUtils.class,
|
||||
ShadowStorageManagerWrapper.class,
|
||||
ShadowStorageManager.class,
|
||||
ShadowUserManager.class
|
||||
})
|
||||
public class SetupFingerprintEnrollIntroductionTest {
|
||||
@@ -89,7 +85,7 @@ public class SetupFingerprintEnrollIntroductionTest {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowStorageManagerWrapper.reset();
|
||||
ShadowStorageManager.reset();
|
||||
ShadowFingerprintManager.reset();
|
||||
}
|
||||
|
||||
@@ -209,7 +205,7 @@ public class SetupFingerprintEnrollIntroductionTest {
|
||||
|
||||
@Test
|
||||
public void testLockPattern() {
|
||||
ShadowStorageManagerWrapper.sIsFileEncrypted = false;
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
|
||||
mController.create().postCreate(null).resume();
|
||||
|
||||
@@ -228,19 +224,4 @@ public class SetupFingerprintEnrollIntroductionTest {
|
||||
private ShadowKeyguardManager getShadowKeyguardManager() {
|
||||
return Shadows.shadowOf(application.getSystemService(KeyguardManager.class));
|
||||
}
|
||||
|
||||
@Implements(StorageManagerWrapper.class)
|
||||
public static class ShadowStorageManagerWrapper {
|
||||
|
||||
private static boolean sIsFileEncrypted = true;
|
||||
|
||||
public static void reset() {
|
||||
sIsFileEncrypted = true;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public static boolean isFileEncryptedNativeOrEmulated() {
|
||||
return sIsFileEncrypted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class DataUsagePreferenceTest {
|
||||
|
||||
@Test
|
||||
public void setTemplate_noDataUsage_shouldDisablePreference() {
|
||||
doReturn(0L).when(mController).getHistoriclUsageLevel(any(NetworkTemplate.class));
|
||||
doReturn(0L).when(mController).getHistoricalUsageLevel(any(NetworkTemplate.class));
|
||||
|
||||
mPreference.setTemplate(
|
||||
NetworkTemplate.buildTemplateMobileWildcard(), 5 /* subId */, null /* services */);
|
||||
@@ -68,7 +68,7 @@ public class DataUsagePreferenceTest {
|
||||
|
||||
@Test
|
||||
public void setTemplate_hasDataUsage_shouldNotDisablePreference() {
|
||||
doReturn(200L).when(mController).getHistoriclUsageLevel(any(NetworkTemplate.class));
|
||||
doReturn(200L).when(mController).getHistoricalUsageLevel(any(NetworkTemplate.class));
|
||||
|
||||
mPreference.setTemplate(
|
||||
NetworkTemplate.buildTemplateMobileWildcard(), 5 /* subId */, null /* services */);
|
||||
|
||||
@@ -493,7 +493,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
mSummaryPreference.setUsageNumbers(1000000L, -1L, true);
|
||||
final String cycleText = "The quick fox";
|
||||
mSummaryPreference.setWifiMode(true, cycleText);
|
||||
doReturn(200L).when(mSummaryPreference).getHistoriclUsageLevel();
|
||||
doReturn(200L).when(mSummaryPreference).getHistoricalUsageLevel();
|
||||
|
||||
bindViewHolder();
|
||||
assertThat(mUsageTitle.getText().toString())
|
||||
@@ -529,7 +529,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
public void testSetWifiMode_noUsageInfo_shouldDisableLaunchButton() {
|
||||
mSummaryPreference = spy(mSummaryPreference);
|
||||
mSummaryPreference.setWifiMode(true, "Test cycle text");
|
||||
doReturn(0L).when(mSummaryPreference).getHistoriclUsageLevel();
|
||||
doReturn(0L).when(mSummaryPreference).getHistoricalUsageLevel();
|
||||
|
||||
bindViewHolder();
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.mobilenetwork;
|
||||
|
||||
import static com.android.settings.mobilenetwork.MobileSettingsActivity.MOBILE_SETTINGS_TAG;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.view.menu.ContextMenuBuilder;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class MobileSettingsActivityTest {
|
||||
|
||||
private static final int CURRENT_SUB_ID = 3;
|
||||
private static final int PREV_SUB_ID = 1;
|
||||
|
||||
private Context mContext;
|
||||
private MobileSettingsActivity mMobileSettingsActivity;
|
||||
private List<SubscriptionInfo> mSubscriptionInfos;
|
||||
private Fragment mShowFragment;
|
||||
private Fragment mHideFragment;
|
||||
|
||||
@Mock
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
@Mock
|
||||
private SubscriptionInfo mSubscriptionInfo;
|
||||
@Mock
|
||||
private FragmentManager mFragmentManager;
|
||||
@Mock
|
||||
private FragmentTransaction mFragmentTransaction;
|
||||
@Mock
|
||||
private BottomNavigationView mBottomNavigationView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
mMobileSettingsActivity = spy(new MobileSettingsActivity());
|
||||
mSubscriptionInfos = new ArrayList<>();
|
||||
mShowFragment = new Fragment();
|
||||
mHideFragment = new Fragment();
|
||||
mMobileSettingsActivity.mSubscriptionInfos = mSubscriptionInfos;
|
||||
|
||||
doReturn(mSubscriptionManager).when(mMobileSettingsActivity).getSystemService(
|
||||
SubscriptionManager.class);
|
||||
doReturn(mBottomNavigationView).when(mMobileSettingsActivity).findViewById(R.id.bottom_nav);
|
||||
doReturn(mFragmentManager).when(mMobileSettingsActivity).getSupportFragmentManager();
|
||||
doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
|
||||
doReturn(mHideFragment).when(mFragmentManager).findFragmentByTag(
|
||||
MOBILE_SETTINGS_TAG + PREV_SUB_ID);
|
||||
doReturn(mShowFragment).when(mFragmentManager).findFragmentByTag(
|
||||
MOBILE_SETTINGS_TAG + CURRENT_SUB_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBottomNavigationView_oneSubscription_shouldBeGone() {
|
||||
mSubscriptionInfos.add(mSubscriptionInfo);
|
||||
doReturn(mSubscriptionInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList();
|
||||
|
||||
mMobileSettingsActivity.updateBottomNavigationView();
|
||||
|
||||
verify(mBottomNavigationView).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBottomNavigationView_twoSubscription_updateMenu() {
|
||||
final Menu menu = new ContextMenuBuilder(mContext);
|
||||
mSubscriptionInfos.add(mSubscriptionInfo);
|
||||
mSubscriptionInfos.add(mSubscriptionInfo);
|
||||
doReturn(mSubscriptionInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList();
|
||||
doReturn(menu).when(mBottomNavigationView).getMenu();
|
||||
|
||||
mMobileSettingsActivity.updateBottomNavigationView();
|
||||
|
||||
assertThat(menu.size()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchFragment_hidePreviousFragment() {
|
||||
mMobileSettingsActivity.mPrevSubscriptionId = PREV_SUB_ID;
|
||||
|
||||
mMobileSettingsActivity.switchFragment(mShowFragment, CURRENT_SUB_ID);
|
||||
|
||||
verify(mFragmentTransaction).hide(mHideFragment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchFragment_fragmentExist_showItWithArguments() {
|
||||
mMobileSettingsActivity.mPrevSubscriptionId = PREV_SUB_ID;
|
||||
|
||||
mMobileSettingsActivity.switchFragment(mShowFragment, CURRENT_SUB_ID);
|
||||
|
||||
assertThat(mShowFragment.getArguments().getInt(
|
||||
MobileSettingsActivity.KEY_SUBSCRIPTION_ID)).isEqualTo(CURRENT_SUB_ID);
|
||||
verify(mFragmentTransaction).show(mShowFragment);
|
||||
}
|
||||
}
|
||||
@@ -16,122 +16,170 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings.Global;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowStorageManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowTheme.class,
|
||||
ShadowLockPatternUtils.class,
|
||||
ShadowStorageManager.class,
|
||||
ShadowUserManager.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
public class ChooseLockGenericTest {
|
||||
|
||||
private Context mContext;
|
||||
private ChooseLockGenericFragment mFragment;
|
||||
private FragmentActivity mActivity;
|
||||
private ChooseLockGeneric mActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mFragment = spy(new ChooseLockGenericFragment());
|
||||
mActivity = mock(FragmentActivity.class);
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
when(mFragment.getFragmentManager()).thenReturn(mock(FragmentManager.class));
|
||||
doNothing().when(mFragment).startActivity(any(Intent.class));
|
||||
Global.putInt(
|
||||
application.getContentResolver(),
|
||||
Global.DEVICE_PROVISIONED, 1);
|
||||
mFragment = new ChooseLockGenericFragment();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Global.putInt(
|
||||
application.getContentResolver(),
|
||||
Global.DEVICE_PROVISIONED, 1);
|
||||
ShadowStorageManager.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = SettingsShadowResources.SettingsShadowTheme.class)
|
||||
public void onCreate_deviceNotProvisioned_shouldFinishActivity() {
|
||||
Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 0);
|
||||
when(mActivity.getContentResolver()).thenReturn(mContext.getContentResolver());
|
||||
when(mActivity.getTheme()).thenReturn(mContext.getTheme());
|
||||
when(mFragment.getArguments()).thenReturn(Bundle.EMPTY);
|
||||
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 0);
|
||||
|
||||
mFragment.onCreate(Bundle.EMPTY);
|
||||
verify(mActivity).finish();
|
||||
initActivity(null);
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_nullIntentData_shouldNotCrash() {
|
||||
doNothing().when(mFragment).updatePreferencesOrFinish(anyBoolean());
|
||||
|
||||
initActivity(null);
|
||||
mFragment.onActivityResult(
|
||||
ChooseLockGenericFragment.CONFIRM_EXISTING_REQUEST, Activity.RESULT_OK,
|
||||
null /* data */);
|
||||
// no crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_passwordTypeSetPin_shouldStartChooseLockPassword() {
|
||||
Intent intent = new Intent().putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
initActivity(intent);
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
|
||||
assertThat(shadowOf(mActivity).getNextStartedActivity()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_passwordTypeSetPinNotFbe_shouldNotStartChooseLock() {
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
Intent intent = new Intent().putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
initActivity(intent);
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
|
||||
assertThat(shadowOf(mActivity).getNextStartedActivity()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode0_shouldNotFinish() {
|
||||
initActivity(null);
|
||||
|
||||
mFragment.onActivityResult(
|
||||
SearchFeatureProvider.REQUEST_CODE, Activity.RESULT_OK, null /* data */);
|
||||
|
||||
verify(mFragment, never()).finish();
|
||||
assertThat(mActivity.isFinishing()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode101_shouldFinish() {
|
||||
initActivity(null);
|
||||
|
||||
mFragment.onActivityResult(
|
||||
ChooseLockGenericFragment.ENABLE_ENCRYPTION_REQUEST, Activity.RESULT_OK,
|
||||
null /* data */);
|
||||
|
||||
verify(mFragment).finish();
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode102_shouldFinish() {
|
||||
initActivity(null);
|
||||
|
||||
mFragment.onActivityResult(
|
||||
ChooseLockGenericFragment.CHOOSE_LOCK_REQUEST, Activity.RESULT_OK, null /* data */);
|
||||
|
||||
verify(mFragment).finish();
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode103_shouldFinish() {
|
||||
initActivity(null);
|
||||
|
||||
mFragment.onActivityResult(
|
||||
ChooseLockGenericFragment.CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST,
|
||||
BiometricEnrollBase.RESULT_FINISHED, null /* data */);
|
||||
|
||||
verify(mFragment).finish();
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode104_shouldFinish() {
|
||||
initActivity(null);
|
||||
|
||||
mFragment.onActivityResult(
|
||||
ChooseLockGenericFragment.SKIP_FINGERPRINT_REQUEST, Activity.RESULT_OK,
|
||||
null /* data */);
|
||||
|
||||
verify(mFragment).finish();
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
private void initActivity(@Nullable Intent intent) {
|
||||
if (intent == null) {
|
||||
intent = new Intent();
|
||||
}
|
||||
intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
|
||||
mActivity = Robolectric.buildActivity(ChooseLockGeneric.InternalActivity.class, intent)
|
||||
.setup().get();
|
||||
mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commitNow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.search;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.os.Binder;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.shadows.ShadowBinder;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DeviceIndexFeatureProviderTest {
|
||||
|
||||
@Mock
|
||||
private JobScheduler mJobScheduler;
|
||||
private DeviceIndexFeatureProvider mProvider;
|
||||
private Activity mActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
ShadowBinder.reset();
|
||||
FakeFeatureFactory.setupForTest();
|
||||
mActivity = spy(Robolectric.buildActivity(Activity.class).create().visible().get());
|
||||
mProvider = spy(new DeviceIndexFeatureProviderImpl());
|
||||
when(mActivity.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowBinder.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_disabled_shouldDoNothing() {
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(false);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
verify(mJobScheduler, never()).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_unprovisioned_shouldDoNothing() {
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 0);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
|
||||
verify(mJobScheduler, never()).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_provisioned_shouldIndex() {
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
verify(mJobScheduler).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_provisioned_newBuild_shouldIndex() {
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
DeviceIndexFeatureProvider.setIndexState(mActivity);
|
||||
Settings.Global.putString(mActivity.getContentResolver(),
|
||||
DeviceIndexFeatureProvider.INDEX_VERSION, "new version");
|
||||
Settings.Global.putString(mActivity.getContentResolver(),
|
||||
DeviceIndexFeatureProvider.LANGUAGE.toString(),
|
||||
DeviceIndexFeatureProvider.INDEX_LANGUAGE);
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
verify(mJobScheduler).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_provisioned_differentUid_shouldNotIndex() {
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
|
||||
ShadowBinder.setCallingUid(Binder.getCallingUid() + 2000);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
verify(mJobScheduler, never()).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_provisioned_newIndex_shouldIndex() {
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
DeviceIndexFeatureProvider.setIndexState(mActivity);
|
||||
Settings.Global.putString(mActivity.getContentResolver(),
|
||||
DeviceIndexFeatureProvider.INDEX_LANGUAGE, "new language");
|
||||
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
verify(mJobScheduler).schedule(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIndex_enabled_provisioned_sameBuild_sameLang_shouldNotIndex() {
|
||||
// Enabled
|
||||
when(mProvider.isIndexingEnabled()).thenReturn(true);
|
||||
// Provisioned
|
||||
Settings.Global.putInt(mActivity.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
// Same build and same language
|
||||
DeviceIndexFeatureProvider.setIndexState(mActivity);
|
||||
|
||||
mProvider.updateIndex(mActivity, false);
|
||||
|
||||
verify(mJobScheduler, never()).schedule(any());
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.search;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceMetadata;
|
||||
import androidx.slice.SliceViewManager;
|
||||
|
||||
import com.android.settings.slices.SettingsSliceProvider;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DeviceIndexUpdateJobServiceTest {
|
||||
private static final Uri BASE_URI = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.build();
|
||||
|
||||
private Activity mActivity;
|
||||
private DeviceIndexUpdateJobService mJob;
|
||||
private SliceViewManager mSliceManager;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
FakeFeatureFactory.setupForTest();
|
||||
mActivity = spy(Robolectric.buildActivity(Activity.class).create().visible().get());
|
||||
mJob = spy(new DeviceIndexUpdateJobService());
|
||||
mSliceManager = mock(SliceViewManager.class);
|
||||
|
||||
doReturn(mActivity.getPackageName()).when(mJob).getPackageName();
|
||||
doReturn(mSliceManager).when(mJob).getSliceViewManager();
|
||||
doNothing().when(mJob).jobFinished(null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetsSlices() {
|
||||
setSlices();
|
||||
|
||||
mJob.updateIndex(null);
|
||||
verify(mSliceManager).getSliceDescendants(eq(BASE_URI));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexesSlices() {
|
||||
setSlices(genSlice("path1"), genSlice("path2"));
|
||||
|
||||
mJob.mRunningJob = true;
|
||||
mJob.updateIndex(null);
|
||||
verify(mSliceManager).getSliceDescendants(eq(BASE_URI));
|
||||
|
||||
DeviceIndexFeatureProvider indexFeatureProvider = FakeFeatureFactory.getFactory(mActivity)
|
||||
.getDeviceIndexFeatureProvider();
|
||||
verify(indexFeatureProvider, times(2)).index(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoNotIndexWithoutTitle() {
|
||||
Slice testSlice = genSlice("path2");
|
||||
setSlices(genSlice("path1"), testSlice);
|
||||
doReturn(null).when(mJob).findTitle(testSlice, mJob.getMetadata(testSlice));
|
||||
|
||||
mJob.mRunningJob = true;
|
||||
mJob.updateIndex(null);
|
||||
verify(mSliceManager).getSliceDescendants(eq(BASE_URI));
|
||||
|
||||
DeviceIndexFeatureProvider indexFeatureProvider = FakeFeatureFactory.getFactory(mActivity)
|
||||
.getDeviceIndexFeatureProvider();
|
||||
verify(indexFeatureProvider, times(1)).index(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopIndexing() {
|
||||
Slice testSlice = genSlice("path1");
|
||||
setSlices(testSlice, genSlice("path2"));
|
||||
mJob.mRunningJob = true;
|
||||
|
||||
doAnswer(invocation -> {
|
||||
// Stop running after the first iteration
|
||||
mJob.mRunningJob = false;
|
||||
return testSlice;
|
||||
}).when(mJob).bindSliceSynchronous(mSliceManager, testSlice.getUri());
|
||||
|
||||
mJob.updateIndex(null);
|
||||
verify(mSliceManager).getSliceDescendants(eq(BASE_URI));
|
||||
|
||||
DeviceIndexFeatureProvider indexFeatureProvider = FakeFeatureFactory.getFactory(mActivity)
|
||||
.getDeviceIndexFeatureProvider();
|
||||
verify(indexFeatureProvider).clearIndex(any());
|
||||
verify(indexFeatureProvider, times(1)).index(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
private Slice genSlice(String path) {
|
||||
return new Slice.Builder(BASE_URI.buildUpon().path(path).build()).build();
|
||||
}
|
||||
|
||||
private void setSlices(Slice... slice) {
|
||||
List<Uri> mUris = new ArrayList<>();
|
||||
for (Slice slouse : slice) {
|
||||
SliceMetadata m = mock(SliceMetadata.class);
|
||||
mUris.add(slouse.getUri());
|
||||
doReturn(slouse).when(mJob).bindSliceSynchronous(mSliceManager, slouse.getUri());
|
||||
doReturn(m).when(mJob).getMetadata(slouse);
|
||||
doReturn(slouse.getUri().getPath()).when(mJob).findTitle(slouse, m);
|
||||
}
|
||||
when(mSliceManager.getSliceDescendants(BASE_URI)).thenReturn(mUris);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import com.android.settings.overlay.DockUpdaterFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.overlay.SupportFeatureProvider;
|
||||
import com.android.settings.overlay.SurveyFeatureProvider;
|
||||
import com.android.settings.search.DeviceIndexFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
import com.android.settings.slices.SlicesFeatureProvider;
|
||||
@@ -62,7 +61,6 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public final UserFeatureProvider userFeatureProvider;
|
||||
public final AssistGestureFeatureProvider assistGestureFeatureProvider;
|
||||
public final AccountFeatureProvider mAccountFeatureProvider;
|
||||
public final DeviceIndexFeatureProvider deviceIndexFeatureProvider;
|
||||
|
||||
public SlicesFeatureProvider slicesFeatureProvider;
|
||||
public SearchFeatureProvider searchFeatureProvider;
|
||||
@@ -104,7 +102,6 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class);
|
||||
slicesFeatureProvider = mock(SlicesFeatureProvider.class);
|
||||
mAccountFeatureProvider = mock(AccountFeatureProvider.class);
|
||||
deviceIndexFeatureProvider = mock(DeviceIndexFeatureProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -186,9 +183,4 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public AccountFeatureProvider getAccountFeatureProvider() {
|
||||
return mAccountFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceIndexFeatureProvider getDeviceIndexFeatureProvider() {
|
||||
return deviceIndexFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,9 @@ public class SettingsRobolectricTestRunner extends RobolectricTestRunner {
|
||||
paths.add(new ResourcePath(null,
|
||||
Fs.fromURL(new URL("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.cardview_cardview-nodeps/android_common/aar/res")), null));
|
||||
paths.add(new ResourcePath(null,
|
||||
Fs.fromURL(new URL("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.slice_slice-view-nodeps/android_common/aar/res")), null));
|
||||
Fs.fromURL(new URL("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.slice_slice-view-nodeps/android_common/aar/res")), null));
|
||||
paths.add(new ResourcePath(null,
|
||||
Fs.fromURL(new URL("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.preference_preference-nodeps/android_common/aar/res")), null));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("SettingsRobolectricTestRunner failure", e);
|
||||
}
|
||||
|
||||
@@ -209,6 +209,10 @@ public class SettingsShadowResources extends ShadowResources {
|
||||
node.setNodeValue("@style/FingerprintLayoutTheme");
|
||||
} else if (attributeValue.startsWith("@*android:string")) {
|
||||
node.setNodeValue("PLACEHOLDER");
|
||||
} else if (attributeValue.startsWith("@*android:dimen")) {
|
||||
node.setNodeValue("321dp");
|
||||
} else if (attributeValue.startsWith("@*android:drawable")) {
|
||||
node.setNodeValue("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class ShadowStorageManager {
|
||||
|
||||
private static boolean sIsUnmountCalled;
|
||||
private static boolean sIsForgetCalled;
|
||||
private static boolean sIsFileEncryptedNativeOrEmulated = true;
|
||||
|
||||
public static boolean isUnmountCalled() {
|
||||
return sIsUnmountCalled;
|
||||
@@ -43,7 +44,7 @@ public class ShadowStorageManager {
|
||||
public static void reset() {
|
||||
sIsUnmountCalled = false;
|
||||
sIsForgetCalled = false;
|
||||
|
||||
sIsFileEncryptedNativeOrEmulated = true;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
@@ -71,6 +72,15 @@ public class ShadowStorageManager {
|
||||
sIsForgetCalled = true;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected static boolean isFileEncryptedNativeOrEmulated() {
|
||||
return sIsFileEncryptedNativeOrEmulated;
|
||||
}
|
||||
|
||||
public static void setIsFileEncryptedNativeOrEmulated(boolean encrypted) {
|
||||
sIsFileEncryptedNativeOrEmulated = encrypted;
|
||||
}
|
||||
|
||||
private VolumeInfo createVolumeInfo(String volumeId) {
|
||||
final DiskInfo disk = new DiskInfo("fakeid", 0);
|
||||
return new VolumeInfo(volumeId, 0, disk, "guid");
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.settings.slices;
|
||||
|
||||
import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
|
||||
import static com.android.settings.slices.SliceDeepLinkSpringBoard.createDeepLink;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
Reference in New Issue
Block a user