Merge changes Ib5ac6ee9,Ib127ad43,I37a423f3 into sc-qpr1-dev

* changes:
  [ProviderModel] Cherry-pick from master ag/15207286, ag/15216391 and ag/15205988
  [Provider Model] Cherry-pick from master ag/15008434, ag/15023697 and ag/15023698
  [ProviderModel] Add InternetDialogController for internet dialog
This commit is contained in:
Zoey Chen
2021-08-24 10:09:52 +00:00
committed by Android (Google) Code Review
36 changed files with 3405 additions and 4 deletions

View File

@@ -0,0 +1,26 @@
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:autoMirrored="true"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="@android:color/black"
android:pathData="M6.23,20.23l1.77,1.77l10,-10l-10,-10l-1.77,1.77l8.23,8.23z"/>
</vector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2H9V6zM18,20H6V10h12V20zM12,17c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2C10,16.1 10.9,17 12,17z"/>
</vector>

View File

@@ -0,0 +1,29 @@
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
</vector>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
android:strokeAlpha="0.3"
android:fillAlpha="0.3"/>
<path
android:fillColor="#FF000000"
android:pathData="M20,10h2v8h-2z"/>
<path
android:fillColor="#FF000000"
android:pathData="M20,20h2v2h-2z"/>
</vector>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="@dimen/settingslib_switch_thumb_margin"
android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"
android:bottom="@dimen/settingslib_switch_thumb_margin">
<shape android:shape="oval">
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
<solid
android:color="@color/settingslib_thumb_off_color"
android:alpha="?android:attr/disabledAlpha"/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="@dimen/settingslib_switch_thumb_margin"
android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"
android:bottom="@dimen/settingslib_switch_thumb_margin">
<shape android:shape="oval">
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
<solid android:color="@color/settingslib_thumb_off_color"/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="@dimen/settingslib_switch_thumb_margin"
android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"
android:bottom="@dimen/settingslib_switch_thumb_margin">
<shape android:shape="oval">
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
<solid android:color="@color/settingslib_state_on_color"/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
<item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
<item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
</selector>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
<item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
<item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
</selector>

View File

@@ -0,0 +1,366 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/internet_connectivity_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/internet_dialog_rounded_top_corner_background"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
style="@style/Widget.SliceView.Panel"
android:gravity="center_vertical|center_horizontal"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/internet_dialog_title"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="google-sans"
android:textSize="24sp"/>
<TextView
android:id="@+id/internet_dialog_subtitle"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:maxLines="1"
android:fontFamily="google-sans"
android:textSize="14sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/scroll_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/internet_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/mobile_network_layout"
android:layout_width="match_parent"
android:layout_height="88dp"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:layout_gravity="center_vertical|start"
android:orientation="horizontal"
android:layout_marginRight="@dimen/settingslib_switchbar_margin"
android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:paddingStart="19dp"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/signal_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<LinearLayout
android:layout_weight="1"
android:id="@+id/mobile_network_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
android:layout_marginLeft="17dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:fontFamily="google-sans"/>
<TextView
android:id="@+id/mobile_summary"
android:layout_marginLeft="17dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
</LinearLayout>
<FrameLayout
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end|center_vertical">
<Switch
android:id="@+id/mobile_toggle"
android:layout_gravity="center"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:track="@drawable/settingslib_track_selector"
android:thumb="@drawable/settingslib_thumb_selector"
android:theme="@style/MainSwitch.Settingslib"/>
</FrameLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/wifi_searching_progress"
android:indeterminate="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="1dp"
android:maxHeight="1dp"
style="@*android:style/Widget.Material.ProgressBar.Horizontal"/>
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginRight="@dimen/settingslib_switchbar_margin"
android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<FrameLayout
android:layout_weight="1"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:text="@string/turn_on_wifi"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:fontFamily="google-sans"/>
</FrameLayout>
<FrameLayout
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<Switch
android:id="@+id/wifi_toggle"
android:layout_gravity="center"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:track="@drawable/settingslib_track_selector"
android:thumb="@drawable/settingslib_thumb_selector"
android:theme="@style/MainSwitch.Settingslib"/>
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/wifi_connected_layout"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_gravity="center_vertical|start"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:layout_marginRight="@dimen/settingslib_switchbar_margin"
android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/wifi_connected_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<LinearLayout
android:layout_weight="3"
android:id="@+id/wifi_connected_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="72dp"
android:gravity="start|center_vertical">
<TextView
android:id="@+id/wifi_connected_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
<TextView
android:id="@+id/wifi_connected_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
</LinearLayout>
<FrameLayout
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_marginRight="5dp"
android:clickable="false"
android:gravity="center">
<ImageView
android:id="@+id/wifi_settings_icon"
android:src="@drawable/ic_settings_24dp"
android:layout_width="24dp"
android:layout_gravity="center"
android:layout_height="wrap_content"/>
</FrameLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/wifi_list_layout"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:nestedScrollingEnabled="false"/>
</LinearLayout>
<LinearLayout
android:id="@+id/see_all_layout"
android:layout_width="match_parent"
android:layout_height="64dp"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical|center_horizontal"
android:orientation="horizontal"
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/arrow_forward"
android:src="@drawable/ic_arrow_forward"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<FrameLayout
android:orientation="vertical"
android:clickable="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp">
<TextView
android:text="@string/see_all_networks"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
</FrameLayout>
</LinearLayout>
<Space
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="28dp"
android:visibility="gone"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginBottom="25dp"
android:gravity="end"
android:orientation="horizontal">
<Button
style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:id="@+id/done"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_marginRight="24dp"
android:layout_gravity="end"
android:background="@drawable/internet_dialog_footer_background"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/inline_done_button"
android:textSize="14sp"
android:fontFamily="google-sans"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/internet_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/wifi_list"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_gravity="center_vertical|start"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/wifi_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<LinearLayout
android:layout_weight="3"
android:id="@+id/wifi_network_layout"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="72dp">
<TextView
android:id="@+id/wifi_title"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center_vertical"
android:gravity="start|center_vertical"
android:layout_marginLeft="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
<TextView
android:id="@+id/wifi_summary"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center_vertical"
android:gravity="start|center_vertical"
android:layout_marginLeft="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:fontFamily="google-sans"/>
</LinearLayout>
<FrameLayout
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_marginRight="@dimen/settingslib_switchbar_padding_right"
android:clickable="false"
android:gravity="center">
<ImageView
android:id="@+id/wifi_locked_icon"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content"/>
</FrameLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -16,7 +16,7 @@
* limitations under the License.
*/
-->
<resources>
<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
<drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
@@ -281,4 +281,16 @@
<color name="wallet_card_border">#33FFFFFF</color>
<color name="people_tile_background">@android:color/system_accent2_50</color>
<!-- Internet Dialog -->
<!-- Material next state on color-->
<color name="settingslib_state_on_color">?androidprv:attr/colorAccentPrimary</color>
<!-- Material next state off color-->
<color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
<!-- Material next track on color-->
<color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
<!-- Material next track off color-->
<color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
<color name="connected_network_primary_color">#ff000000</color>
<color name="connected_network_tertiary_color">#808080</color>
</resources>

View File

@@ -1590,4 +1590,35 @@
<!-- The padding between the icon and the text. -->
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_margin">12dp</dimen>
<dimen name="internet_dialog_list_max_height">614dp</dimen>
<!-- Signal icon in internet dialog -->
<dimen name="signal_strength_icon_size">24dp</dimen>
<!-- Internet dialog related dimensions -->
<dimen name="internet_dialog_corner_radius">24dp</dimen>
<!-- Size of internet dialog -->
<dimen name="settingslib_switchbar_margin">16dp</dimen>
<!-- Minimum width of switch -->
<dimen name="settingslib_min_switch_width">48dp</dimen>
<!-- Size of layout margin left -->
<dimen name="settingslib_switchbar_padding_left">20dp</dimen>
<!-- Size of layout margin right -->
<dimen name="settingslib_switchbar_padding_right">20dp</dimen>
<!-- Radius of switch bar -->
<dimen name="settingslib_switch_bar_radius">24dp</dimen>
<!-- Margin of switch thumb -->
<dimen name="settingslib_switch_thumb_margin">4dp</dimen>
<!-- Size of switch thumb -->
<dimen name="settingslib_switch_thumb_size">16dp</dimen>
<!-- Width of switch track -->
<dimen name="settingslib_switch_track_width">48dp</dimen>
<!-- Height of switch track -->
<dimen name="settingslib_switch_track_height">24dp</dimen>
<!-- Radius of switch track -->
<dimen name="settingslib_switch_track_radius">31dp</dimen>
</resources>

View File

@@ -2994,4 +2994,37 @@
<string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
<!-- URL for more information about changes in global actions -->
<string name="global_actions_change_url" translatable="false"></string>
<!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
<string name="airplane_mode">Airplane mode</string>
<!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
<string name="mobile_data_settings_title">Mobile data</string>
<!-- Provider Model: Summary text separator for preferences including a short description
(eg. "Connected / 5G"). [CHAR LIMIT=50] -->
<string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
<!-- Provider Model:
Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_connection_active">Connected</string>
<!-- Provider Model:
Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
<!-- Provider Model:
Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
<string name="mobile_data_no_connection">No connection</string>
<!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
<string name="non_carrier_network_unavailable">No other networks available</string>
<!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
<string name="all_network_unavailable">No networks available</string>
<!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
<string name="turn_on_wifi">Wi\u2011Fi</string>
<!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
<string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
<!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
<string name="tap_a_network_to_connect">Tap a network to connect</string>
<!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
<string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
<!-- Provider Model: Failure notification for connect -->
<string name="wifi_failed_connect_message">Failed to connect to network</string>
<!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
<string name="see_all_networks">See all</string>
</resources>

View File

@@ -903,4 +903,43 @@
<!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
<style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
</style>
<style name="Widget.SliceView.Panel">
<item name="titleSize">16sp</item>
<item name="rowStyle">@style/SliceRow</item>
<item name="android:background">?android:attr/colorBackgroundFloating</item>
</style>
<style name="SliceRow">
<!-- 2dp start padding for the start icon -->
<item name="titleItemStartPadding">2dp</item>
<item name="titleItemEndPadding">0dp</item>
<!-- Padding between content and the start icon is 14dp -->
<item name="contentStartPadding">14dp</item>
<!-- Padding between content and end items is 16dp -->
<item name="contentEndPadding">16dp</item>
<!-- Both side margins of end item are 16dp -->
<item name="endItemStartPadding">16dp</item>
<item name="endItemEndPadding">16dp</item>
<!-- Both side margins of bottom divider are 12dp -->
<item name="bottomDividerStartPadding">12dp</item>
<item name="bottomDividerEndPadding">12dp</item>
<item name="actionDividerHeight">32dp</item>
</style>
<style name="Theme.SystemUI.Dialog.Internet">
<item name="android:windowBackground">@drawable/internet_dialog_background</item>
</style>
<style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
<item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
</style>
</resources>

View File

@@ -65,6 +65,7 @@ import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -364,6 +365,7 @@ public class Dependency {
@Inject Lazy<UiEventLogger> mUiEventLogger;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
@Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
@Inject
public Dependency() {
@@ -578,6 +580,7 @@ public class Dependency {
mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
mProviders.put(EdgeBackGestureHandler.Factory.class,
mEdgeBackGestureHandlerFactoryLazy::get);
mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);

View File

@@ -28,6 +28,7 @@ import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.Html;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
import android.widget.Switch;
@@ -51,6 +52,7 @@ import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
@@ -75,6 +77,8 @@ public class InternetTile extends QSTileImpl<SignalState> {
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
private final InternetDialogFactory mInternetDialogFactory;
final Handler mHandler;
@Inject
public InternetTile(
@@ -86,10 +90,13 @@ public class InternetTile extends QSTileImpl<SignalState> {
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
NetworkController networkController
NetworkController networkController,
InternetDialogFactory internetDialogFactory
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mInternetDialogFactory = internetDialogFactory;
mHandler = mainHandler;
mController = networkController;
mDataController = mController.getMobileDataController();
mController.observe(getLifecycle(), mSignalCallback);
@@ -114,7 +121,13 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
protected void handleClick(@Nullable View view) {
mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
} else {
mHandler.post(() -> {
mInternetDialogFactory.create(true);
});
}
}
@Override

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.qs.tiles.dialog;
import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* Adapter for showing Wi-Fi networks.
*/
public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
private static final String TAG = "InternetAdapter";
private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final InternetDialogController mInternetDialogController;
protected View mHolderView;
protected Context mContext;
public InternetAdapter(InternetDialogController controller) {
mInternetDialogController = controller;
}
@Override
public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) {
mContext = viewGroup.getContext();
mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
viewGroup, false);
return new InternetViewHolder(mHolderView, mInternetDialogController);
}
@Override
public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
List<WifiEntry> wifiList = getWifiEntryList();
if (wifiList != null && wifiList.size() != 0) {
int count = getItemCount();
if (wifiList.size() > count) {
wifiList = getWifiEntryList().subList(0, count - 1);
}
if (position < wifiList.size()) {
viewHolder.onBind(wifiList.get(position));
}
} else if (DEBUG) {
Log.d(TAG, "onBindViewHolder, Wi-Fi entry list = null");
}
}
private List<WifiEntry> getWifiEntryList() {
if (mInternetDialogController.getWifiEntryList() == null) {
return null;
}
return mInternetDialogController.getWifiEntryList().stream()
.filter(wifiAp -> wifiAp.getConnectedState()
!= WifiEntry.CONNECTED_STATE_CONNECTED)
.limit(getItemCount())
.collect(Collectors.toList());
}
/**
* The total number of networks (mobile network and entries of Wi-Fi) should be four in
* {@link InternetDialog}.
*
* Airplane mode is ON (mobile network is gone):
* Return four Wi-Fi's entries if no connected Wi-Fi.
* Return three Wi-Fi's entries if one connected Wi-Fi.
* Airplane mode is OFF (mobile network is visible):
* Return three Wi-Fi's entries if no connected Wi-Fi.
* Return two Wi-Fi's entries if one connected Wi-Fi.
*
* @return The total number of networks.
*/
@Override
public int getItemCount() {
boolean hasConnectedWifi = mInternetDialogController.getConnectedWifiEntry() != null;
if (mInternetDialogController.isAirplaneModeEnabled()) {
return hasConnectedWifi ? 3 : 4;
} else {
return hasConnectedWifi ? 2 : 3;
}
}
/**
* ViewHolder for binding Wi-Fi view.
*/
static class InternetViewHolder extends RecyclerView.ViewHolder {
final LinearLayout mContainerLayout;
final LinearLayout mWifiListLayout;
final LinearLayout mWifiNetworkLayout;
final ImageView mWifiIcon;
final TextView mWifiTitleText;
final TextView mWifiSummaryText;
final ImageView mWifiLockedIcon;
final Context mContext;
final InternetDialogController mInternetDialogController;
InternetViewHolder(View view, InternetDialogController internetDialogController) {
super(view);
mContext = view.getContext();
mInternetDialogController = internetDialogController;
mContainerLayout = view.requireViewById(R.id.internet_container);
mWifiListLayout = view.requireViewById(R.id.wifi_list);
mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
mWifiIcon = view.requireViewById(R.id.wifi_icon);
mWifiTitleText = view.requireViewById(R.id.wifi_title);
mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
mWifiLockedIcon = view.requireViewById(R.id.wifi_locked_icon);
}
void onBind(WifiEntry wifiEntry) {
int security = wifiEntry.getSecurity();
try {
mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
if (isOpenNetwork(security)) {
mWifiLockedIcon.setVisibility(View.GONE);
} else {
mWifiLockedIcon.setVisibility(View.VISIBLE);
mWifiLockedIcon.setImageDrawable(
mContext.getDrawable(R.drawable.ic_friction_lock_closed));
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
setWifiNetworkLayout(wifiEntry.getTitle(),
Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
mWifiListLayout.setOnClickListener(v -> {
if (wifiEntry.shouldEditBeforeConnect()) {
final Intent intent = new Intent(ACTION_WIFI_DIALOG);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
mContext.startActivity(intent);
}
mInternetDialogController.connect(wifiEntry);
});
}
/** Return true if this is an open network AccessPoint. */
boolean isOpenNetwork(int security) {
return security == SECURITY_NONE
|| security == SECURITY_OWE;
}
void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
mWifiNetworkLayout.setVisibility(View.VISIBLE);
mWifiTitleText.setText(title);
if (TextUtils.isEmpty(summary)) {
mWifiTitleText.setGravity(Gravity.CENTER);
mWifiSummaryText.setVisibility(View.GONE);
return;
} else {
mWifiTitleText.setGravity(Gravity.BOTTOM);
mWifiSummaryText.setGravity(Gravity.TOP);
mWifiSummaryText.setVisibility(View.VISIBLE);
}
mWifiSummaryText.setText(summary);
}
Drawable getWifiDrawable(WifiEntry wifiEntry) throws Throwable {
Drawable drawable = mContext.getDrawable(
com.android.internal.R.drawable.ic_wifi_signal_0);
AtomicReference<Drawable> shared = new AtomicReference<>();
final @ColorInt int tint = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorControlNormal);
Drawable signalDrawable = mContext.getDrawable(
Utils.getWifiIconResource(wifiEntry.getLevel()));
signalDrawable.setTint(tint);
shared.set(signalDrawable);
drawable = shared.get();
return drawable;
}
}
}

View File

@@ -0,0 +1,553 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.qs.tiles.dialog;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Space;
import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
*/
@SysUISingleton
public class InternetDialog extends SystemUIDialog implements
InternetDialogController.InternetDialogCallback, Window.Callback {
private static final String TAG = "InternetDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Handler mHandler;
private final LinearLayoutManager mLayoutManager;
private final Runnable mHideProgressBarRunnable = () -> {
setProgressBarVisible(false);
};
@VisibleForTesting
protected InternetAdapter mAdapter;
@VisibleForTesting
protected WifiManager mWifiManager;
@VisibleForTesting
protected View mDialogView;
private InternetDialogFactory mInternetDialogFactory;
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
private AlertDialog mAlertDialog;
private UiEventLogger mUiEventLogger;
private Context mContext;
private InternetDialogController mInternetDialogController;
private TextView mInternetDialogTitle;
private TextView mInternetDialogSubTitle;
private ProgressBar mProgressBar;
private LinearLayout mInternetListLayout;
private LinearLayout mConnectedWifListLayout;
private LinearLayout mConnectedWifList;
private LinearLayout mMobileNetworkLayout;
private LinearLayout mMobileNetworkList;
private LinearLayout mTurnWifiOnLayout;
private LinearLayout mSeeAllLayout;
private Space mSpace;
private RecyclerView mWifiRecyclerView;
private ImageView mConnectedWifiIcon;
private ImageView mWifiSettingsIcon;
private TextView mConnectedWifiTitleText;
private TextView mConnectedWifiSummaryText;
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
private Switch mMobileDataToggle;
private Switch mWiFiToggle;
private Button mDoneButton;
private Drawable mBackgroundOn;
private WifiEntry mConnectedWifiEntry;
private int mListMaxHeight;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mIsProgressBarVisible;
private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
// Set max height for list
if (mInternetListLayout.getHeight() > mListMaxHeight) {
ViewGroup.LayoutParams params = mInternetListLayout.getLayoutParams();
params.height = mListMaxHeight;
mInternetListLayout.setLayoutParams(params);
}
};
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
InternetDialogController internetDialogController,
boolean aboveStatusBar, UiEventLogger uiEventLogger, @Main Handler handler) {
super(context, R.style.Theme_SystemUI_Dialog_Internet);
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
mContext = context;
mHandler = handler;
mInternetDialogFactory = internetDialogFactory;
mInternetDialogController = internetDialogController;
mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
mTelephonyManager = mInternetDialogController.getTelephonyManager();
mWifiManager = mInternetDialogController.getWifiManager();
mLayoutManager = new LinearLayoutManager(mContext) {
@Override
public boolean canScrollVertically() {
return false;
}
};
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.internet_dialog_list_max_height);
mUiEventLogger = uiEventLogger;
mAdapter = new InternetAdapter(mInternetDialogController);
if (!aboveStatusBar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG) {
Log.d(TAG, "onCreate");
}
mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
null);
final Window window = getWindow();
final WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.gravity = Gravity.BOTTOM;
// Move down the dialog to overlay the navigation bar.
layoutParams.setFitInsetsTypes(
layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
layoutParams.setFitInsetsSides(WindowInsets.Side.all());
layoutParams.setFitInsetsIgnoringVisibility(true);
window.setAttributes(layoutParams);
window.setContentView(mDialogView);
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
window.setWindowAnimations(R.style.Animation_InternetDialog);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.addFlags(FLAG_LAYOUT_NO_LIMITS);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
mSpace = mDialogView.requireViewById(R.id.space);
mDoneButton = mDialogView.requireViewById(R.id.done);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mInternetListLayoutListener);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
mWifiRecyclerView.setLayoutManager(mLayoutManager);
mWifiRecyclerView.setAdapter(mAdapter);
}
@Override
public void onStart() {
super.onStart();
if (DEBUG) {
Log.d(TAG, "onStart");
}
mInternetDialogController.onStart(this);
}
@Override
public void onStop() {
super.onStop();
if (DEBUG) {
Log.d(TAG, "onStop");
}
mHandler.removeCallbacks(mHideProgressBarRunnable);
mMobileNetworkLayout.setOnClickListener(null);
mMobileDataToggle.setOnCheckedChangeListener(null);
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
mDoneButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@Override
public void dismissDialog() {
if (DEBUG) {
Log.d(TAG, "dismissDialog");
}
mInternetDialogFactory.destroyDialog();
dismiss();
}
void updateDialog() {
if (DEBUG) {
Log.d(TAG, "updateDialog");
}
if (mInternetDialogController.isAirplaneModeEnabled()) {
mInternetDialogSubTitle.setVisibility(View.GONE);
} else {
mInternetDialogSubTitle.setText(getSubtitleText());
}
showProgressBar();
setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
setConnectedWifiLayout();
boolean isWifiEnabled = mWifiManager.isWifiEnabled();
mWiFiToggle.setChecked(isWifiEnabled);
int visible = isWifiEnabled ? View.VISIBLE : View.GONE;
mWifiRecyclerView.setVisibility(visible);
mAdapter.notifyDataSetChanged();
mSeeAllLayout.setVisibility(visible);
mSpace.setVisibility(isWifiEnabled ? View.GONE : View.VISIBLE);
}
private void setOnClickListener() {
mMobileNetworkLayout.setOnClickListener(v -> {
if (mInternetDialogController.isMobileDataEnabled()) {
if (!mInternetDialogController.activeNetworkIsCellular()) {
mInternetDialogController.connectCarrierNetwork();
}
}
});
mMobileDataToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
if (!isChecked && shouldShowMobileDialog()) {
showTurnOffMobileDialog();
} else if (!shouldShowMobileDialog()) {
mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
isChecked, false);
}
});
mConnectedWifListLayout.setOnClickListener(v -> {
// TODO(b/191475923): Need to launch the detailed page of Wi-Fi entry.
});
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
mSpace.setVisibility(isChecked ? View.GONE : View.VISIBLE);
});
mDoneButton.setOnClickListener(v -> dismiss());
}
private void setMobileDataLayout(boolean isCellularNetwork) {
if (mInternetDialogController.isAirplaneModeEnabled()
|| !mInternetDialogController.hasCarrier()) {
mMobileNetworkLayout.setVisibility(View.GONE);
} else {
mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileNetworkLayout.setVisibility(View.VISIBLE);
mMobileTitleText.setText(getMobileNetworkTitle());
mMobileSummaryText.setText(
Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
int titleColor = isCellularNetwork ? mContext.getColor(
R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
mContext, android.R.attr.textColorPrimary);
int summaryColor = isCellularNetwork ? mContext.getColor(
R.color.connected_network_tertiary_color) : Utils.getColorAttrDefaultColor(
mContext, android.R.attr.textColorTertiary);
mMobileTitleText.setTextColor(titleColor);
mMobileSummaryText.setTextColor(summaryColor);
mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
}
}
private void setConnectedWifiLayout() {
if (!mWifiManager.isWifiEnabled()
|| mInternetDialogController.getConnectedWifiEntry() == null) {
mConnectedWifListLayout.setBackground(null);
mConnectedWifListLayout.setVisibility(View.GONE);
return;
}
mConnectedWifListLayout.setVisibility(View.VISIBLE);
mConnectedWifiTitleText.setText(getConnectedWifiTitle());
mConnectedWifiSummaryText.setText(getConnectedWifiSummary());
mConnectedWifiIcon.setImageDrawable(getConnectedWifiDrawable());
mConnectedWifiTitleText.setTextColor(
mContext.getColor(R.color.connected_network_primary_color));
mConnectedWifiSummaryText.setTextColor(
mContext.getColor(R.color.connected_network_tertiary_color));
mWifiSettingsIcon.setColorFilter(
mContext.getColor(R.color.connected_network_primary_color));
mConnectedWifListLayout.setBackground(mBackgroundOn);
}
void onClickSeeMoreButton() {
mInternetDialogController.launchNetworkSetting();
}
CharSequence getDialogTitleText() {
return mInternetDialogController.getDialogTitleText();
}
CharSequence getSubtitleText() {
return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
}
private Drawable getConnectedWifiDrawable() {
try {
return mInternetDialogController.getWifiConnectedDrawable(mConnectedWifiEntry);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
private Drawable getSignalStrengthDrawable() {
return mInternetDialogController.getSignalStrengthDrawable();
}
CharSequence getMobileNetworkTitle() {
return mInternetDialogController.getMobileNetworkTitle();
}
String getMobileNetworkSummary() {
return mInternetDialogController.getMobileNetworkSummary();
}
String getConnectedWifiTitle() {
return mInternetDialogController.getConnectedWifiTitle();
}
String getConnectedWifiSummary() {
return mInternetDialogController.getConnectedWifiSummary();
}
private void showProgressBar() {
if (mWifiManager == null || !mWifiManager.isWifiEnabled()) {
setProgressBarVisible(false);
return;
}
setProgressBarVisible(true);
List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
if (wifiScanResults != null && wifiScanResults.size() > 0) {
mContext.getMainThreadHandler().postDelayed(mHideProgressBarRunnable,
2000 /* delay millis */);
}
}
private void setProgressBarVisible(boolean visible) {
if (mWifiManager.isWifiEnabled() && mAdapter.mHolderView != null
&& mAdapter.mHolderView.isAttachedToWindow()) {
mIsProgressBarVisible = true;
}
mIsProgressBarVisible = visible;
mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.INVISIBLE);
mInternetDialogSubTitle.setText(getSubtitleText());
}
private boolean shouldShowMobileDialog() {
boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
false);
if (mInternetDialogController.isMobileDataEnabled() && !flag) {
return true;
}
return false;
}
private void showTurnOffMobileDialog() {
CharSequence carrierName =
mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
boolean isInService = mInternetDialogController.isVoiceStateInService();
if (TextUtils.isEmpty(carrierName) || !isInService) {
carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
}
mAlertDialog = new Builder(mContext)
.setTitle(R.string.mobile_data_disable_title)
.setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
.setNegativeButton(android.R.string.cancel, (d, w) -> {
mMobileDataToggle.setChecked(true);
})
.setPositiveButton(
com.android.internal.R.string.alert_windows_notification_turn_off_action,
(d, w) -> {
mInternetDialogController.setMobileDataEnabled(mContext,
mDefaultDataSubId, false, false);
mMobileDataToggle.setChecked(false);
Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
})
.create();
mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true));
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog);
mAlertDialog.show();
}
@Override
public void onRefreshCarrierInfo() {
mHandler.post(() -> updateDialog());
}
@Override
public void onSimStateChanged() {
mHandler.post(() -> updateDialog());
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
mHandler.post(() -> updateDialog());
}
@Override
public void onSubscriptionsChanged(int defaultDataSubId) {
mDefaultDataSubId = defaultDataSubId;
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mHandler.post(() -> updateDialog());
}
@Override
public void onServiceStateChanged(ServiceState serviceState) {
mHandler.post(() -> updateDialog());
}
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
mAdapter.notifyDataSetChanged();
mHandler.post(() -> updateDialog());
}
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
mHandler.post(() -> updateDialog());
}
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
mHandler.post(() -> updateDialog());
}
@Override
public void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry) {
mConnectedWifiEntry = connectedEntry;
mAdapter.notifyDataSetChanged();
mHandler.post(() -> updateDialog());
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
if (!hasFocus && isShowing()) {
dismiss();
}
}
}
@Override
public void onWifiStateReceived(Context context, Intent intent) {
if (intent == null) {
return;
}
String action = intent.getAction();
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
mInternetDialogController.scanWifiAccessPoints();
showProgressBar();
return;
}
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mHandler.post(() -> updateDialog());
}
}
public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The Internet dialog became visible on the screen.")
INTERNET_DIALOG_SHOW(843);
private final int mId;
InternetDialogEvent(int id) {
mId = id;
}
@Override
public int getId() {
return mId;
}
}
}

View File

@@ -0,0 +1,851 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.qs.tiles.dialog;
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
import android.annotation.ColorInt;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.DeviceInfoUtils;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
public class InternetDialogController implements WifiEntry.DisconnectCallback,
NetworkController.AccessPointController.AccessPointCallback {
private static final String TAG = "InternetDialogController";
private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
"android.settings.NETWORK_PROVIDER_SETTINGS";
private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
public static final int NO_CELL_DATA_TYPE_ICON = 0;
private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
R.string.tap_a_network_to_connect;
private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
R.string.wifi_empty_list_wifi_on;
private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
R.string.non_carrier_network_unavailable;
private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
R.string.all_network_unavailable;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private WifiManager mWifiManager;
private Context mContext;
private ActivityStarter mActivityStarter;
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
private ConnectivityManager mConnectivityManager;
private TelephonyDisplayInfo mTelephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
private Handler mHandler;
private MobileMappings.Config mConfig = null;
private Executor mExecutor;
private AccessPointController mAccessPointController;
private IntentFilter mWifiStateFilter;
private InternetDialogCallback mCallback;
private List<WifiEntry> mWifiEntry;
private WifiEntry mConnectedEntry;
private UiEventLogger mUiEventLogger;
private BroadcastDispatcher mBroadcastDispatcher;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@VisibleForTesting
protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@VisibleForTesting
protected InternetTelephonyCallback mInternetTelephonyCallback;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshCarrierInfo() {
mCallback.onRefreshCarrierInfo();
}
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
mCallback.onSimStateChanged();
}
};
protected List<SubscriptionInfo> getSubscriptionInfo() {
return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
}
@Inject
public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
ActivityStarter starter, AccessPointController accessPointController,
SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
GlobalSettings globalSettings) {
if (DEBUG) {
Log.d(TAG, "Init InternetDialogController");
}
mHandler = handler;
mExecutor = mainExecutor;
mContext = context;
mGlobalSettings = globalSettings;
mWifiManager = wifiManager;
mTelephonyManager = telephonyManager;
mConnectivityManager = connectivityManager;
mSubscriptionManager = subscriptionManager;
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mUiEventLogger = uiEventLogger;
mActivityStarter = starter;
mAccessPointController = accessPointController;
mConfig = MobileMappings.Config.readConfig(mContext);
}
void onStart(@NonNull InternetDialogCallback callback) {
if (DEBUG) {
Log.d(TAG, "onStart");
}
mCallback = callback;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
mAccessPointController.addAccessPointCallback(this);
mBroadcastDispatcher.registerReceiver(mWifiStateReceiver, mWifiStateFilter, mExecutor);
// Listen the subscription changes
mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mOnSubscriptionsChangedListener);
mDefaultDataSubId = getDefaultDataSubscriptionId();
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mInternetTelephonyCallback = new InternetTelephonyCallback();
mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
// Listen the connectivity changes
mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), new DataConnectivityListener(), mHandler);
scanWifiAccessPoints();
}
void onStop() {
if (DEBUG) {
Log.d(TAG, "onStop");
}
mBroadcastDispatcher.unregisterReceiver(mWifiStateReceiver);
mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
mSubscriptionManager.removeOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
}
@VisibleForTesting
boolean isAirplaneModeEnabled() {
return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
@VisibleForTesting
protected int getDefaultDataSubscriptionId() {
return mSubscriptionManager.getDefaultDataSubscriptionId();
}
@VisibleForTesting
protected Intent getSettingsIntent() {
return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
CharSequence getDialogTitleText() {
if (isAirplaneModeEnabled()) {
return mContext.getText(R.string.airplane_mode);
}
return mContext.getText(R.string.quick_settings_internet_label);
}
CharSequence getSubtitleText(boolean isProgressBarVisible) {
if (isAirplaneModeEnabled()) {
return null;
}
if (!mWifiManager.isWifiEnabled()) {
// When the airplane mode is off and Wi-Fi is disabled.
// Sub-Title: Wi-Fi is off
if (DEBUG) {
Log.d(TAG, "Airplane mode off + Wi-Fi off.");
}
return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
}
if (isProgressBarVisible) {
// When the Wi-Fi scan result callback is received
// Sub-Title: Searching for networks...
return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
}
final List<ScanResult> wifiList = mWifiManager.getScanResults();
if (wifiList != null && wifiList.size() != 0) {
return mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT);
}
// Sub-Title:
// show non_carrier_network_unavailable
// - while Wi-Fi on + no Wi-Fi item
// - while Wi-Fi on + no Wi-Fi item + mobile data off
// show all_network_unavailable:
// - while Wi-Fi on + no Wi-Fi item + no carrier item
// - while Wi-Fi on + no Wi-Fi item + service is out of service
// - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
if (DEBUG) {
Log.d(TAG, "No Wi-Fi item.");
}
if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
if (DEBUG) {
Log.d(TAG, "No carrier or service is out of service.");
}
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
if (!isMobileDataEnabled()) {
if (DEBUG) {
Log.d(TAG, "Mobile data off");
}
return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
}
if (!activeNetworkIsCellular()) {
if (DEBUG) {
Log.d(TAG, "No carrier data.");
}
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
}
Drawable getWifiConnectedDrawable(WifiEntry wifiEntry) throws Throwable {
final @ColorInt int tint;
tint = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.colorAccentPrimaryVariant);
final Drawable drawable = mContext.getDrawable(
com.android.settingslib.Utils.getWifiIconResource(wifiEntry.getLevel()));
drawable.setTint(tint);
return drawable;
}
Drawable getSignalStrengthDrawable() {
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
try {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null");
}
return drawable;
}
if (isDataStateInService() || isVoiceStateInService()) {
AtomicReference<Drawable> shared = new AtomicReference<>();
shared.set(getSignalStrengthDrawableWithLevel());
drawable = shared.get();
}
drawable.setTint(
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal));
if (activeNetworkIsCellular()) {
drawable.setTint(Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.colorAccentPrimaryVariant));
}
} catch (Throwable e) {
e.printStackTrace();
}
return drawable;
}
/**
* To get the signal bar icon with level.
*
* @return The Drawable which is a signal bar icon with level.
*/
Drawable getSignalStrengthDrawableWithLevel() {
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
level += 1;
numLevels += 1;
}
return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
}
Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
int iconType, boolean cutOut) {
Log.d(TAG, "getSignalStrengthIcon");
final SignalDrawable signalDrawable = new SignalDrawable(context);
signalDrawable.setLevel(
SignalDrawable.getState(level, numLevels, cutOut));
// Make the network type drawable
final Drawable networkDrawable =
iconType == NO_CELL_DATA_TYPE_ICON
? EMPTY_DRAWABLE
: context.getResources().getDrawable(iconType, context.getTheme());
// Overlay the two drawables
final Drawable[] layers = {networkDrawable, signalDrawable};
final int iconSize =
context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
final LayerDrawable icons = new LayerDrawable(layers);
// Set the network type icon at the top left
icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
// Set the signal strength icon at the bottom right
icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
return icons;
}
private boolean shouldInflateSignalStrength(int subId) {
return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
}
private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
return displayNames.getOrDefault(subscriptionId, "");
}
private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
}
// Map of SubscriptionId to DisplayName
final Supplier<Stream<DisplayInfo>> originalInfos =
() -> getSubscriptionInfo()
.stream()
.filter(i -> {
// Filter out null values.
return (i != null && i.getDisplayName() != null);
})
.map(i -> {
DisplayInfo info = new DisplayInfo();
info.subscriptionInfo = i;
info.originalName = i.getDisplayName().toString().trim();
return info;
});
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
// Return the set of duplicate names
final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
.filter(info -> !uniqueNames.add(info.originalName))
.map(info -> info.originalName)
.collect(Collectors.toSet());
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
if (duplicateOriginalNames.contains(info.originalName)) {
// This may return null, if the user cannot view the phone number itself.
final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
info.subscriptionInfo);
String lastFourDigits = "";
if (phoneNumber != null) {
lastFourDigits = (phoneNumber.length() > 4)
? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
}
if (TextUtils.isEmpty(lastFourDigits)) {
info.uniqueName = info.originalName;
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
}
} else {
info.uniqueName = info.originalName;
}
return info;
});
// Check uniqueness a second time.
// We might not have had permission to view the phone numbers.
// There might also be multiple phone numbers whose last 4 digits the same.
uniqueNames.clear();
final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
.filter(info -> !uniqueNames.add(info.uniqueName))
.map(info -> info.uniqueName)
.collect(Collectors.toSet());
return uniqueInfos.get().map(info -> {
if (duplicatePhoneNames.contains(info.uniqueName)) {
info.uniqueName = info.originalName + " "
+ info.subscriptionInfo.getSubscriptionId();
}
return info;
}).collect(Collectors.toMap(
info -> info.subscriptionInfo.getSubscriptionId(),
info -> info.uniqueName));
}
CharSequence getMobileNetworkTitle() {
return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
}
String getMobileNetworkSummary() {
String description = getNetworkTypeDescription(mContext, mConfig,
mTelephonyDisplayInfo, mDefaultDataSubId);
return getMobileSummary(mContext, mTelephonyManager, description);
}
/**
* Get currently description of mobile network type.
*/
private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
String iconKey = getIconKey(telephonyDisplayInfo);
if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
if (DEBUG) {
Log.d(TAG, "The description of network type is empty.");
}
return "";
}
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
return resId != 0
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
private String getMobileSummary(Context context, TelephonyManager telephonyManager,
String networkTypeDescription) {
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
if (!isDataStateInService()) {
return context.getString(R.string.mobile_data_no_connection);
}
String summary = networkTypeDescription;
if (activeNetworkIsCellular()) {
summary = context.getString(R.string.preference_summary_default_combination,
context.getString(R.string.mobile_data_connection_active),
networkTypeDescription);
}
return summary;
}
String getConnectedWifiTitle() {
if (getConnectedWifiEntry() == null) {
if (DEBUG) {
Log.d(TAG, "connected entry is null");
}
return "";
}
return getConnectedWifiEntry().getTitle();
}
String getConnectedWifiSummary() {
if (getConnectedWifiEntry() == null) {
if (DEBUG) {
Log.d(TAG, "connected entry is null");
}
return "";
}
return getConnectedWifiEntry().getSummary(false);
}
void launchNetworkSetting() {
mCallback.dismissDialog();
mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
}
void connectCarrierNetwork() {
final MergedCarrierEntry mergedCarrierEntry =
mAccessPointController.getMergedCarrierEntry();
if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
mergedCarrierEntry.connect(null /* ConnectCallback */);
}
}
List<WifiEntry> getWifiEntryList() {
return mWifiEntry;
}
WifiEntry getConnectedWifiEntry() {
return mConnectedEntry;
}
WifiManager getWifiManager() {
return mWifiManager;
}
TelephonyManager getTelephonyManager() {
return mTelephonyManager;
}
SubscriptionManager getSubscriptionManager() {
return mSubscriptionManager;
}
/**
* @return whether there is the carrier item in the slice.
*/
boolean hasCarrier() {
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
}
return false;
}
if (isAirplaneModeEnabled() || mTelephonyManager == null
|| mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
return false;
}
return true;
}
/**
* Return {@code true} if mobile data is enabled
*/
boolean isMobileDataEnabled() {
if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
return false;
}
return true;
}
/**
* Set whether to enable data for {@code subId}, also whether to disable data for other
* subscription
*/
void setMobileDataEnabled(Context context, int subId, boolean enabled,
boolean disableOtherSubscriptions) {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
}
return;
}
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
}
return;
}
mTelephonyManager.setDataEnabled(enabled);
if (disableOtherSubscriptions) {
final List<SubscriptionInfo> subInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
if (subInfoList != null) {
for (SubscriptionInfo subInfo : subInfoList) {
// We never disable mobile data for opportunistic subscriptions.
if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
context.getSystemService(TelephonyManager.class).createForSubscriptionId(
subInfo.getSubscriptionId()).setDataEnabled(false);
}
}
}
}
}
boolean isDataStateInService() {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not detect mobile state.");
}
return false;
}
return mTelephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED;
}
boolean isVoiceStateInService() {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
}
return false;
}
final ServiceState serviceState = mTelephonyManager.getServiceState();
return serviceState != null
&& serviceState.getState() == serviceState.STATE_IN_SERVICE;
}
boolean activeNetworkIsCellular() {
if (mConnectivityManager == null) {
if (DEBUG) {
Log.d(TAG, "ConnectivityManager is null, can not check active network.");
}
return false;
}
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
return false;
}
final NetworkCapabilities networkCapabilities =
mConnectivityManager.getNetworkCapabilities(activeNetwork);
if (networkCapabilities == null) {
return false;
}
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
}
boolean connect(WifiEntry ap) {
if (ap == null) {
if (DEBUG) {
Log.d(TAG, "No Wi-Fi ap to connect.");
}
return false;
}
if (ap.getWifiConfiguration() != null) {
if (DEBUG) {
Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
}
} else {
if (DEBUG) {
Log.d(TAG, "connect to unsaved network " + ap.getTitle());
}
}
ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
return false;
}
static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
final ActivityStarter mActivityStarter;
final Context mContext;
final WifiEntry mWifiEntry;
WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
WifiEntry connectWifiEntry) {
mActivityStarter = activityStarter;
mContext = context;
mWifiEntry = connectWifiEntry;
}
@Override
public void onConnectResult(@ConnectStatus int status) {
if (DEBUG) {
Log.d(TAG, "onConnectResult " + status);
}
if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startActivity(intent, true);
} else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
Toast.makeText(mContext, R.string.wifi_failed_connect_message,
Toast.LENGTH_SHORT).show();
} else {
if (DEBUG) {
Log.d(TAG, "connect failure reason=" + status);
}
}
}
}
void scanWifiAccessPoints() {
mAccessPointController.scanForAccessPoints();
}
@Override
public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
if (accessPoints == null) {
return;
}
boolean hasConnectedWifi = false;
mWifiEntry = accessPoints;
for (WifiEntry wifiEntry : accessPoints) {
if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
mConnectedEntry = wifiEntry;
hasConnectedWifi = true;
break;
}
}
if (!hasConnectedWifi) {
mConnectedEntry = null;
}
mCallback.onAccessPointsChanged(mWifiEntry, mConnectedEntry);
}
@Override
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
@Override
public void onDisconnectResult(int status) {
}
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.SignalStrengthsListener {
@Override
public void onServiceStateChanged(@NonNull ServiceState serviceState) {
mCallback.onServiceStateChanged(serviceState);
}
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
mCallback.onDataConnectionStateChanged(state, networkType);
}
@Override
public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
mCallback.onSignalStrengthsChanged(signalStrength);
}
@Override
public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
mTelephonyDisplayInfo = telephonyDisplayInfo;
mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
}
}
private class InternetOnSubscriptionChangedListener
extends SubscriptionManager.OnSubscriptionsChangedListener {
InternetOnSubscriptionChangedListener() {
super();
}
@Override
public void onSubscriptionsChanged() {
mDefaultDataSubId = getDefaultDataSubscriptionId();
if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mTelephonyManager.registerTelephonyCallback(mHandler::post,
mInternetTelephonyCallback);
mCallback.onSubscriptionsChanged(mDefaultDataSubId);
}
}
}
private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
@Override
public void onCapabilitiesChanged(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities) {
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork != null && activeNetwork.equals(network)) {
// update UI
mCallback.onCapabilitiesChanged(network, networkCapabilities);
}
}
}
private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mCallback.onWifiStateReceived(context, intent);
}
};
interface InternetDialogCallback {
void onRefreshCarrierInfo();
void onSimStateChanged();
void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
void onSubscriptionsChanged(int defaultDataSubId);
void onServiceStateChanged(ServiceState serviceState);
void onDataConnectionStateChanged(int state, int networkType);
void onSignalStrengthsChanged(SignalStrength signalStrength);
void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
void dismissDialog();
void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry);
void onWifiStateReceived(Context context, Intent intent);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.qs.tiles.dialog
import android.content.Context
import android.os.Handler
import android.util.Log
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
private const val TAG = "InternetDialogFactory"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/**
* Factory to create [InternetDialog] objects.
*/
@SysUISingleton
class InternetDialogFactory @Inject constructor(
@Main private val handler: Handler,
private val internetDialogController: InternetDialogController,
private val context: Context,
private val uiEventLogger: UiEventLogger
) {
companion object {
var internetDialog: InternetDialog? = null
}
/** Creates a [InternetDialog]. */
fun create(aboveStatusBar: Boolean) {
if (internetDialog != null) {
if (DEBUG) {
Log.d(TAG, "InternetDialog is showing, do not create it twice.")
}
return
} else {
internetDialog = InternetDialog(context, this, internetDialogController, aboveStatusBar,
uiEventLogger, handler)
internetDialog?.show()
}
}
fun destroyDialog() {
if (DEBUG) {
Log.d(TAG, "destroyDialog")
}
internetDialog = null
}
}

View File

@@ -0,0 +1,14 @@
package com.android.systemui.qs.tiles.dialog;
import android.content.Context;
import android.util.FeatureFlagUtils;
public class InternetDialogUtil {
public static boolean isProviderModelEnabled(Context context) {
if (context == null) {
return false;
}
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
}

View File

@@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
@@ -156,6 +157,15 @@ public class AccessPointControllerImpl
fireAcccessPointsCallback(entries);
}
@Override
public MergedCarrierEntry getMergedCarrierEntry() {
if (mWifiPickerTracker == null) {
fireAcccessPointsCallback(Collections.emptyList());
return null;
}
return mWifiPickerTracker.getMergedCarrierEntry();
}
@Override
public int getIcon(WifiEntry ap) {
int level = ap.getLevel();

View File

@@ -23,6 +23,7 @@ import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -223,6 +224,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
MergedCarrierEntry getMergedCarrierEntry();
int getIcon(WifiEntry ap);
boolean connect(WifiEntry ap);
boolean canConfigWifi();

View File

@@ -68,9 +68,12 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -191,6 +194,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
private boolean mUserSetup;
private boolean mSimDetected;
private boolean mForceCellularValidated;
private InternetDialogFactory mInternetDialogFactory;
private Handler mMainHandler;
private ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -221,7 +226,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
FeatureFlags featureFlags,
DumpManager dumpManager) {
DumpManager dumpManager,
@Main Handler handler,
InternetDialogFactory internetDialogFactory) {
this(context, connectivityManager,
telephonyManager,
telephonyListenerManager,
@@ -242,6 +249,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
featureFlags,
dumpManager);
mReceiverHandler.post(mRegisterListeners);
mMainHandler = handler;
mInternetDialogFactory = internetDialogFactory;
}
@VisibleForTesting
@@ -480,6 +489,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
if (InternetDialogUtil.isProviderModelEnabled(mContext)) {
filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
}
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -788,6 +800,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
mMainHandler.post(() -> mInternetDialogFactory.create(true));
break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);

View File

@@ -0,0 +1,102 @@
package com.android.systemui.qs.tiles.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.view.View;
import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.wifitrackerlib.WifiEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class InternetAdapterTest extends SysuiTestCase {
private static final String WIFI_TITLE = "Wi-Fi Title";
private static final String WIFI_SUMMARY = "Wi-Fi Summary";
private InternetDialogController mInternetDialogController = mock(
InternetDialogController.class);
private InternetAdapter mInternetAdapter;
private InternetAdapter.InternetViewHolder mViewHolder;
@Mock
private WifiEntry mWifiEntry = mock(WifiEntry.class);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mInternetAdapter = new InternetAdapter(mInternetDialogController);
mViewHolder = (InternetAdapter.InternetViewHolder) mInternetAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
}
@Test
public void getItemCount_withApmOnWifiOnNoConnectedWifi_returnFour() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(4);
}
@Test
public void getItemCount_withApmOnWifiOnHasConnectedWifi_returnThree() {
when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
}
@Test
public void getItemCount_withApmOffWifiOnNoConnectedWifi_returnThree() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
}
@Test
public void getItemCount_withApmOffWifiOnHasConnectedWifi_returnTwo() {
when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(2);
}
@Test
public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() {
when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
mInternetAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() {
when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
mInternetAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.VISIBLE);
}
}

View File

@@ -0,0 +1,269 @@
package com.android.systemui.qs.tiles.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wifitrackerlib.WifiEntry;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class InternetDialogControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private MockInternetDialogController mInternetDialogController;
private InternetDialogController.InternetDialogCallback mCallback =
mock(InternetDialogController.InternetDialogCallback.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
private WifiManager mWifiManager = mock(WifiManager.class);
private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
private Handler mHandler;
@Mock
private GlobalSettings mGlobalSettings;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private NetworkController.AccessPointController mAccessPointController;
@Mock
private WifiEntry mWifiEntryConnected = mock(WifiEntry.class);
@Mock
private WifiInfo mWifiInfo;
@Mock
private ServiceState mServiceState;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
mInternetDialogController = new MockInternetDialogController(mContext, mUiEventLogger,
mStarter, mAccessPointController, mSubscriptionManager, mTelephonyManager,
mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
mKeyguardUpdateMonitor, mGlobalSettings);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mCallback);
}
@Test
public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
mInternetDialogController.setAirplaneModeEnabled(true);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("airplane_mode")));
}
@Test
public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
mInternetDialogController.setAirplaneModeEnabled(false);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("quick_settings_internet_label")));
}
@Test
public void getSubtitleText_withAirplaneModeOn_returnNull() {
mInternetDialogController.setAirplaneModeEnabled(true);
assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
}
@Test
public void getSubtitleText_withWifiOff_returnWifiIsOff() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(false);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("wifi_is_off")));
}
@Test
public void getSubtitleText_withWifiOn_returnSearchWifi() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(true),
getResourcesString("wifi_empty_list_wifi_on")));
}
@Test
public void getSubtitleText_withWifiEntry_returnTapToConnect() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = mock(ArrayList.class);
doReturn(1).when(wifiScanResults).size();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("tap_a_network_to_connect")));
}
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = new ArrayList<>();
doReturn(wifiScanResults).when(mWifiManager).getScanResults();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
when(mSubscriptionManager.getActiveSubscriptionIdList())
.thenReturn(new int[] {SUB_ID});
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
}
@Test
public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = new ArrayList<>();
doReturn(wifiScanResults).when(mWifiManager).getScanResults();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
when(mSubscriptionManager.getActiveSubscriptionIdList())
.thenReturn(new int[] {SUB_ID});
doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("non_carrier_network_unavailable")));
}
@Test
public void getConnectedWifiTitle_withNoConnectedEntry_returnNull() {
mInternetDialogController.setConnectedWifiEntry(null);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
""));
}
@Test
public void getConnectedWifiTitle_withConnectedEntry_returnTitle() {
mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
when(mWifiEntryConnected.getTitle()).thenReturn(CONNECTED_TITLE);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
CONNECTED_TITLE));
}
@Test
public void getConnectedWifiSummary_withNoConnectedEntry_returnNull() {
mInternetDialogController.setConnectedWifiEntry(null);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
""));
}
@Test
public void getConnectedWifiSummary_withConnectedEntry_returnSummary() {
mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
when(mWifiEntryConnected.getSummary(false)).thenReturn(CONNECTED_SUMMARY);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
CONNECTED_SUMMARY));
}
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
private int getResourcesId(String name) {
return mContext.getResources().getIdentifier(name, "string",
mContext.getPackageName());
}
private class MockInternetDialogController extends InternetDialogController {
private WifiEntry mConnectedEntry;
private GlobalSettings mGlobalSettings;
private boolean mIsAirplaneModeOn;
MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
ActivityStarter starter, AccessPointController accessPointController,
SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings) {
super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
broadcastDispatcher, keyguardUpdateMonitor, globalSettings);
mGlobalSettings = globalSettings;
}
@Override
boolean isAirplaneModeEnabled() {
return mIsAirplaneModeOn;
}
public void setAirplaneModeEnabled(boolean enabled) {
mIsAirplaneModeOn = enabled;
}
@Override
WifiEntry getConnectedWifiEntry() {
return mConnectedEntry;
}
public void setConnectedWifiEntry(WifiEntry connectedEntry) {
mConnectedEntry = connectedEntry;
}
}
}

View File

@@ -0,0 +1,246 @@
package com.android.systemui.qs.tiles.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wifitrackerlib.WifiEntry;
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 java.util.Arrays;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class InternetDialogTest extends SysuiTestCase {
private static final int SUB_ID = 1;
private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
private static final String WIFI_TITLE = "Connected Wi-Fi Title";
private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private InternetDialogFactory mInternetDialogFactory = mock(InternetDialogFactory.class);
private InternetAdapter mInternetAdapter = mock(InternetAdapter.class);
private InternetDialogController mInternetDialogController = mock(
InternetDialogController.class);
private InternetDialogController.InternetDialogCallback mCallback =
mock(InternetDialogController.InternetDialogCallback.class);
private MockInternetDialog mInternetDialog;
private WifiReceiver mWifiReceiver = null;
private WifiManager mMockWifiManager = mock(WifiManager.class);
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
@Mock
private WifiEntry mWifiEntry = mock(WifiEntry.class);
@Mock
private WifiInfo mWifiInfo;
@Mock
private Handler mHandler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mInternetDialog = new MockInternetDialog(mContext, mInternetDialogFactory,
mInternetDialogController, true, mUiEventLogger, mHandler);
mInternetDialog.show();
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
when(mMockWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
mInternetDialog.setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
mInternetDialog.setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
mInternetDialog.setConnectedWifiTitle(WIFI_TITLE);
mInternetDialog.setConnectedWifiSummary(WIFI_SUMMARY);
mWifiReceiver = new WifiReceiver();
IntentFilter mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mContext.registerReceiver(mWifiReceiver, mIntentFilter);
when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
}
@After
public void tearDown() {
mInternetDialog.dismissDialog();
}
@Test
public void updateDialog_withApmOn_internetDialogSubTitleGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
mInternetDialog.updateDialog();
final TextView view = mInternetDialog.mDialogView.requireViewById(
R.id.internet_dialog_subtitle);
assertThat(view.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
mInternetDialog.updateDialog();
final TextView view = mInternetDialog.mDialogView.requireViewById(
R.id.internet_dialog_subtitle);
assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void updateDialog_withApmOn_mobileDataLayoutGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
mInternetDialog.updateDialog();
final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
R.id.mobile_network_layout);
assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void updateDialog_withWifiOnAndHasConnectedWifi_connectedWifiLayoutVisible() {
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
mInternetDialog.updateDialog();
final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
R.id.wifi_connected_layout);
assertThat(linearLayout.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void updateDialog_withWifiOnAndNoConnectedWifi_connectedWifiLayoutGone() {
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
mInternetDialog.updateDialog();
final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
R.id.wifi_connected_layout);
assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void updateDialog_withWifiOff_WifiRecycleViewGone() {
when(mMockWifiManager.isWifiEnabled()).thenReturn(false);
mInternetDialog.updateDialog();
final RecyclerView view = mInternetDialog.mDialogView.requireViewById(
R.id.wifi_list_layout);
assertThat(view.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onClickSeeMoreButton_clickSeeMore_verifyLaunchNetworkSetting() {
final LinearLayout seeAllLayout = mInternetDialog.mDialogView.requireViewById(
R.id.see_all_layout);
seeAllLayout.performClick();
verify(mInternetDialogController).launchNetworkSetting();
}
private class MockInternetDialog extends InternetDialog {
private String mMobileNetworkTitle;
private String mMobileNetworkSummary;
private String mConnectedWifiTitle;
private String mConnectedWifiSummary;
MockInternetDialog(Context context, InternetDialogFactory internetDialogFactory,
InternetDialogController internetDialogController, boolean aboveStatusBar,
UiEventLogger uiEventLogger, @Main Handler handler) {
super(context, internetDialogFactory, internetDialogController, aboveStatusBar,
uiEventLogger, handler);
mAdapter = mInternetAdapter;
mWifiManager = mMockWifiManager;
}
@Override
String getMobileNetworkTitle() {
return mMobileNetworkTitle;
}
@Override
String getMobileNetworkSummary() {
return mMobileNetworkSummary;
}
void setMobileNetworkTitle(String title) {
mMobileNetworkTitle = title;
}
void setMobileNetworkSummary(String summary) {
mMobileNetworkSummary = summary;
}
@Override
String getConnectedWifiTitle() {
return mConnectedWifiTitle;
}
@Override
String getConnectedWifiSummary() {
return mConnectedWifiSummary;
}
void setConnectedWifiTitle(String title) {
mConnectedWifiTitle = title;
}
void setConnectedWifiSummary(String summary) {
mConnectedWifiSummary = summary;
}
@Override
public void onWifiStateReceived(Context context, Intent intent) {
setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
}
}
private class WifiReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
return;
}
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mInternetDialog.updateDialog();
}
}
}
}