SystemUI: Add hotspot toggle in QS internet dialog

Inspired by nothing ;)

Shrink down the dialog a bit to make way for it.

Change-Id: I49799061f82c3e22b384b34a7220d269b2206d20
diff --git a/packages/SystemUI/res/drawable/ic_internet_hotspot.xml b/packages/SystemUI/res/drawable/ic_internet_hotspot.xml
new file mode 100644
index 0000000..efe34a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_internet_hotspot.xml
@@ -0,0 +1,11 @@
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:pathData="M 10 8.7 C 8.9 8.7 8 9.6 8 10.7 C 8 11.8 8.9 12.7 10 12.7 C 11.1 12.7 12 11.8 12 10.7 C 12 9.6 11.1 8.7 10 8.7 Z M 16 10.7 C 16 7.39 13.31 4.7 10 4.7 C 6.69 4.7 4 7.39 4 10.7 C 4 12.92 5.21 14.85 7 15.89 L 8 14.15 C 6.81 13.45 6 12.18 6 10.7 C 6 8.49 7.79 6.7 10 6.7 C 12.21 6.7 14 8.49 14 10.7 C 14 12.18 13.19 13.45 12 14.15 L 13 15.89 C 14.79 14.85 16 12.92 16 10.7 Z M 10 0.7 C 4.48 0.7 0 5.18 0 10.7 C 0 14.4 2.01 17.62 4.99 19.35 L 5.99 17.62 C 3.61 16.23 2 13.66 2 10.7 C 2 6.28 5.58 2.7 10 2.7 C 14.42 2.7 18 6.28 18 10.7 C 18 13.66 16.39 16.23 14 17.62 L 15 19.35 C 17.99 17.62 20 14.4 20 10.7 C 20 5.18 15.52 0.7 10 0.7 Z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml b/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml
new file mode 100644
index 0000000..2d30019
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml
@@ -0,0 +1,11 @@
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="21dp"
+    android:height="21dp"
+    android:viewportWidth="21"
+    android:viewportHeight="21"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:pathData="M 1.61 0.81 L 0.19 2.22 L 2.88 4.91 C 1.58 6.6 0.8 8.71 0.8 11 C 0.8 13.76 1.92 16.26 3.73 18.07 L 5.15 16.65 C 3.7 15.21 2.8 13.21 2.8 11 C 2.8 9.25 3.37 7.65 4.31 6.34 L 5.74 7.77 C 5.15 8.7 4.8 9.81 4.8 11 C 4.8 12.66 5.48 14.15 6.56 15.24 L 7.98 13.82 C 7.25 13.1 6.8 12.11 6.8 11 C 6.8 10.37 6.95 9.77 7.21 9.24 L 8.82 10.85 C 8.82 10.9 8.8 10.95 8.8 11 C 8.8 11.55 9.03 12.05 9.39 12.41 C 9.75 12.77 10.25 13 10.8 13 C 10.85 13 10.9 12.99 10.96 12.98 L 18.58 20.6 L 19.99 19.19 L 1.61 0.81 Z M 16.5 12.87 C 16.69 12.28 16.8 11.65 16.8 11 C 16.8 7.69 14.11 5 10.8 5 C 10.15 5 9.52 5.1 8.93 5.3 L 10.64 7.01 C 10.69 7 10.75 7 10.8 7 C 13.01 7 14.8 8.79 14.8 11 C 14.8 11.05 14.8 11.11 14.79 11.16 L 16.5 12.87 Z M 10.8 3 C 15.22 3 18.8 6.58 18.8 11 C 18.8 12.22 18.53 13.37 18.03 14.4 L 19.52 15.89 C 20.33 14.45 20.8 12.78 20.8 11 C 20.8 5.48 16.32 1 10.8 1 C 9.02 1 7.36 1.46 5.91 2.28 L 7.39 3.76 C 8.43 3.27 9.58 3 10.8 3 Z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 16eba22..438d394 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -127,7 +127,8 @@
 
                 <LinearLayout
                     android:id="@+id/mobile_network_layout"
-                    style="@style/InternetDialog.Network">
+                    style="@style/InternetDialog.Network"
+                    android:layout_height="72dp">
 
                     <FrameLayout
                         android:layout_width="24dp"
@@ -195,10 +196,84 @@
                   android:layout="@layout/qs_dialog_secondary_mobile_network"
                   style="@style/InternetDialog.Network"/>
 
+                <Space
+                    android:id="@+id/mobile_connected_space"
+                    android:layout_height="8dp"
+                    android:layout_width="match_parent"
+                    android:visibility="gone"/>
+
+                <LinearLayout
+                    android:id="@+id/hotspot_layout"
+                    style="@style/InternetDialog.Network"
+                    android:layout_height="72dp">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:clickable="false"
+                        android:layout_gravity="center_vertical|start">
+                        <ImageView
+                            android:id="@+id/hotspot_icon"
+                            android:autoMirrored="true"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:src="@drawable/ic_internet_hotspot_disabled"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_weight="1"
+                        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/hotspot_title"
+                            android:maxLines="1"
+                            style="@style/InternetDialog.NetworkTitle"/>
+                        <TextView
+                            android:id="@+id/hotspot_summary"
+                            style="@style/InternetDialog.NetworkSummary"/>
+                    </LinearLayout>
+
+                    <View
+                        android:id="@+id/hotspot_toggle_divider"
+                        android:layout_width="1dp"
+                        android:layout_height="28dp"
+                        android:layout_marginStart="7dp"
+                        android:layout_marginEnd="16dp"
+                        android:layout_gravity="center_vertical"
+                        android:background="?android:attr/textColorSecondary"/>
+
+                    <FrameLayout
+                        android:layout_width="@dimen/settingslib_switch_track_width"
+                        android:layout_height="48dp"
+                        android:layout_gravity="end|center_vertical">
+                        <Switch
+                            android:id="@+id/hotspot_toggle"
+                            android:contentDescription="@string/quick_settings_hotspot_label"
+                            android:switchMinWidth="@dimen/settingslib_switch_track_width"
+                            android:layout_gravity="center"
+                            android:layout_width="@dimen/settingslib_switch_track_width"
+                            android:layout_height="match_parent"
+                            android:track="@drawable/settingslib_track_selector"
+                            android:thumb="@drawable/settingslib_thumb_selector"
+                            android:theme="@style/MainSwitch.Settingslib"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <Space
+                    android:id="@+id/wifi_connected_space"
+                    android:layout_height="8dp"
+                    android:layout_width="match_parent"
+                    android:visibility="gone"/>
+
                 <LinearLayout
                     android:id="@+id/turn_on_wifi_layout"
                     style="@style/InternetDialog.Network"
-                    android:layout_height="@dimen/internet_dialog_wifi_network_height"
+                    android:layout_height="@dimen/internet_dialog_wifi_toggle_height"
                     android:gravity="center"
                     android:clickable="false"
                     android:focusable="false">
@@ -250,6 +325,7 @@
                     android:id="@+id/wifi_connected_layout"
                     style="@style/InternetDialog.Network"
                     android:layout_height="@dimen/internet_dialog_wifi_network_height"
+                    android:layout_marginTop="8dp"
                     android:paddingStart="20dp"
                     android:paddingEnd="24dp"
                     android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -313,7 +389,8 @@
             <LinearLayout
                 android:id="@+id/see_all_layout"
                 style="@style/InternetDialog.Network"
-                android:layout_height="64dp"
+                android:layout_height="56dp"
+                android:paddingTop="8dp"
                 android:paddingStart="20dp">
 
                 <FrameLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2852403..734b79d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1658,6 +1658,8 @@
     <dimen name="internet_dialog_list_max_height">662dp</dimen>
     <!-- The height of the WiFi network in Internet panel. -->
     <dimen name="internet_dialog_wifi_network_height">72dp</dimen>
+    <!-- The height of the WiFi toggle layout in Internet panel -->
+    <dimen name="internet_dialog_wifi_toggle_height">56dp</dimen>
 
     <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
     <dimen name="large_dialog_width">@dimen/match_parent</dimen>
diff --git a/packages/SystemUI/res/values/leaf_strings.xml b/packages/SystemUI/res/values/leaf_strings.xml
index 88200f3..04dc126 100644
--- a/packages/SystemUI/res/values/leaf_strings.xml
+++ b/packages/SystemUI/res/values/leaf_strings.xml
@@ -19,4 +19,10 @@
     <string name="qs_data_switch_text_2">SIM 2</string>
     <string name="qs_data_switch_changed_1">Switched data card to SIM 1.</string>
     <string name="qs_data_switch_changed_2">Switched data card to SIM 2.</string>
+
+    <!-- QuickSettings: InternetDialog: Summary for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
+    <plurals name="quick_settings_internet_hotspot_summary_num_devices">
+        <item quantity="one">%d device connected</item>
+        <item quantity="other">%d devices connected</item>
+    </plurals>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 9d214e7..3297f25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -17,12 +17,15 @@
 
 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
 import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
 
 import android.app.AlertDialog;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.telephony.ServiceState;
@@ -38,6 +41,7 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
@@ -115,6 +119,7 @@
     private LinearLayout mSecondaryMobileNetworkLayout;
     private LinearLayout mTurnWifiOnLayout;
     private LinearLayout mEthernetLayout;
+    private LinearLayout mHotspotLayout;
     private TextView mWifiToggleTitleText;
     private LinearLayout mWifiScanNotifyLayout;
     private TextView mWifiScanNotifyText;
@@ -132,7 +137,13 @@
     private TextView mAirplaneModeSummaryText;
     private Switch mMobileDataToggle;
     private View mMobileToggleDivider;
+    private View mMobileConnectedSpace;
+    private ImageView mHotspotIcon;
+    private TextView mHotspotTitleText;
+    private TextView mHotspotSummaryText;
+    private Switch mHotspotToggle;
     private Switch mWiFiToggle;
+    private View mWifiConnectedSpace;
     private Button mDoneButton;
 
     @VisibleForTesting
@@ -218,6 +229,7 @@
         mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
         mEthernetLayout = mDialogView.requireViewById(R.id.ethernet_layout);
         mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+        mHotspotLayout = mDialogView.requireViewById(R.id.hotspot_layout);
         mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
         mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
         mWifiScanNotifyLayout = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
@@ -238,12 +250,19 @@
         mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
         mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
         mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+        mMobileConnectedSpace = mDialogView.requireViewById(R.id.mobile_connected_space);
+        mHotspotIcon = mDialogView.requireViewById(R.id.hotspot_icon);
+        mHotspotTitleText = mDialogView.requireViewById(R.id.hotspot_title);
+        mHotspotSummaryText = mDialogView.requireViewById(R.id.hotspot_summary);
+        mHotspotToggle = mDialogView.requireViewById(R.id.hotspot_toggle);
         mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+        mWifiConnectedSpace = mDialogView.requireViewById(R.id.wifi_connected_space);
         mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
         mInternetDialogTitle.setText(getDialogTitleText());
         mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
         mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
         setOnClickListener();
+        setHotspotLayout();
         mTurnWifiOnLayout.setBackground(null);
         mAirplaneModeButton.setVisibility(
                 mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
@@ -278,6 +297,8 @@
             Log.d(TAG, "onStop");
         }
         mMobileNetworkLayout.setOnClickListener(null);
+        mHotspotLayout.setOnClickListener(null);
+        mHotspotToggle.setOnCheckedChangeListener(null);
         mConnectedWifListLayout.setOnClickListener(null);
         if (mSecondaryMobileNetworkLayout != null) {
             mSecondaryMobileNetworkLayout.setOnClickListener(null);
@@ -305,8 +326,10 @@
      *
      * @param shouldUpdateMobileNetwork {@code true} for update the mobile network layout,
      *                                  otherwise {@code false}.
+     * @param shouldUpdateHotspot {@code true} for update the hotspot layout,
+     *                                  otherwise {@code false}.
      */
-    void updateDialog(boolean shouldUpdateMobileNetwork) {
+    void updateDialog(boolean shouldUpdateMobileNetwork, boolean shouldUpdateHotspot) {
         if (DEBUG) {
             Log.d(TAG, "updateDialog");
         }
@@ -321,6 +344,10 @@
                     mInternetDialogController.isCarrierNetworkActive());
         }
 
+        if (shouldUpdateHotspot) {
+            setHotspotLayout();
+        }
+
         if (!mCanConfigWifi) {
             return;
         }
@@ -334,6 +361,10 @@
         updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
     }
 
+    void updateDialog(boolean shouldUpdateMobileNetwork) {
+        updateDialog(shouldUpdateMobileNetwork, false /* shouldUpdateHotspot */);
+    }
+
     private void setOnClickListener() {
         mMobileNetworkLayout.setOnClickListener(v -> {
             int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
@@ -352,6 +383,9 @@
                         isChecked, false);
             }
         });
+        mHotspotLayout.setOnClickListener(mInternetDialogController::launchHotspotSetting);
+        mHotspotToggle.setOnCheckedChangeListener(
+                (buttonView, isChecked) -> mInternetDialogController.setHotspotEnabled(isChecked));
         mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
         mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
         mWiFiToggle.setOnCheckedChangeListener(
@@ -412,6 +446,8 @@
                     mSignalIcon.setImageDrawable(drawable);
                 });
             });
+            mMobileConnectedSpace.setVisibility(
+                    isNetworkConnected ? View.VISIBLE : View.GONE);
 
             mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
             mMobileToggleDivider.setVisibility(
@@ -499,6 +535,26 @@
         }
     }
 
+    private void setHotspotLayout() {
+        if (!mInternetDialogController.isHotspotAvailable()) {
+            mHotspotLayout.setVisibility(View.GONE);
+            return;
+        }
+        mHotspotLayout.setVisibility(View.VISIBLE);
+        mHotspotTitleText.setText(getHotspotTitle());
+        mHotspotSummaryText.setText(getHotspotSummary());
+
+        boolean enabled = mInternetDialogController.isHotspotEnabled();
+        mHotspotIcon.setImageResource(enabled ? R.drawable.ic_internet_hotspot
+                : R.drawable.ic_internet_hotspot_disabled);
+        mHotspotToggle.setChecked(enabled);
+
+        boolean dataSaver = mInternetDialogController.isDataSaverEnabled();
+        mHotspotTitleText.setEnabled(!dataSaver);
+        mHotspotSummaryText.setEnabled(!dataSaver);
+        mHotspotToggle.setEnabled(!dataSaver);
+    }
+
     @MainThread
     private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
         if (mWiFiToggle.isChecked() != isWifiEnabled) {
@@ -509,8 +565,15 @@
                     ? R.style.TextAppearance_InternetDialog_Active
                     : R.style.TextAppearance_InternetDialog);
         }
-        mTurnWifiOnLayout.setBackground(
-                (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+
+        boolean showBackground = isDeviceLocked && mConnectedWifiEntry != null;
+        ViewGroup.LayoutParams lp = mTurnWifiOnLayout.getLayoutParams();
+        lp.height = mContext.getResources().getDimensionPixelSize(
+                showBackground ? R.dimen.internet_dialog_wifi_network_height
+                : R.dimen.internet_dialog_wifi_toggle_height);
+        mTurnWifiOnLayout.setLayoutParams(lp);
+        mTurnWifiOnLayout.setBackground(showBackground ? mBackgroundOn : null);
+        mWifiConnectedSpace.setVisibility(showBackground ? View.VISIBLE : View.GONE);
 
         if (!mCanChangeWifiState && mWiFiToggle.isEnabled()) {
             mWiFiToggle.setEnabled(false);
@@ -645,6 +708,35 @@
         return mInternetDialogController.getMobileNetworkSummary(subId);
     }
 
+    private CharSequence getHotspotTitle() {
+        final WifiManager wifiManager = mInternetDialogController.getWifiManager();
+        if (wifiManager != null) {
+            final SoftApConfiguration softApConfig = wifiManager.getSoftApConfiguration();
+            if (softApConfig != null) {
+                return softApConfig.getSsid();
+            }
+        }
+        return mContext.getString(R.string.quick_settings_hotspot_label);
+    }
+
+    String getHotspotSummary() {
+        if (mInternetDialogController.isDataSaverEnabled()) {
+            return mContext.getString(
+                    R.string.quick_settings_hotspot_secondary_label_data_saver_enabled);
+        } else if (mInternetDialogController.isHotspotTransient()) {
+            return mContext.getString(R.string.quick_settings_hotspot_secondary_label_transient);
+        } else if (mInternetDialogController.isHotspotEnabled()) {
+            int numDevices = mInternetDialogController.getHotspotNumDevices();
+            if (numDevices > 0) {
+                return mContext.getResources().getQuantityString(
+                        R.plurals.quick_settings_internet_hotspot_summary_num_devices,
+                        numDevices, numDevices);
+            }
+            return mContext.getString(R.string.switch_bar_on);
+        }
+        return mContext.getString(R.string.switch_bar_off);
+    }
+
     private void setProgressBarVisible(boolean visible) {
         if (mIsProgressBarVisible == visible) {
             return;
@@ -802,6 +894,12 @@
     }
 
     @Override
+    public void onHotspotChanged() {
+        mHandler.post(() -> updateDialog(false /* shouldUpdateMobileNetwork */,
+                true /* shouldUpdateHotspot */));
+    }
+
+    @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
         if (mAlertDialog != null && !mAlertDialog.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 592cb3b..7049014 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -35,8 +35,10 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.net.ConnectivityManager;
+import android.net.INetworkPolicyListener;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Handler;
@@ -75,6 +77,7 @@
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
+import com.android.systemui.Dependency;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -85,6 +88,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.connectivity.AccessPointController;
+import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.toast.SystemUIToast;
@@ -193,6 +197,9 @@
     private boolean mHasWifiEntries;
     private WifiStateWorker mWifiStateWorker;
 
+    private final HotspotController mHotspotController;
+    private final NetworkPolicyManager mPolicyManager;
+
     @VisibleForTesting
     static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
     @VisibleForTesting
@@ -233,6 +240,26 @@
                 }
             };
 
+    private final HotspotController.Callback mHotspotCallback =
+            new HotspotController.Callback() {
+                @Override
+                public void onHotspotChanged(boolean enabled, int numDevices) {
+                    mCallback.onHotspotChanged();
+                }
+
+                @Override
+                public void onHotspotAvailabilityChanged(boolean available) {
+                    mCallback.onHotspotChanged();
+                }
+            };
+
+    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
+        @Override
+        public void onRestrictBackgroundChanged(final boolean isDataSaving) {
+            mCallback.onHotspotChanged();
+        }
+    };
+
     protected List<SubscriptionInfo> getSubscriptionInfo() {
         return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo();
     }
@@ -251,6 +278,7 @@
             LocationController locationController,
             DialogLaunchAnimator dialogLaunchAnimator,
             WifiStateWorker wifiStateWorker,
+            HotspotController hotspotController,
             FeatureFlags featureFlags
     ) {
         if (DEBUG) {
@@ -285,6 +313,8 @@
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
         mWifiStateWorker = wifiStateWorker;
+        mHotspotController = hotspotController;
+        mPolicyManager = NetworkPolicyManager.from(context);
         mFeatureFlags = featureFlags;
     }
 
@@ -297,6 +327,8 @@
         mAccessPointController.addAccessPointCallback(this);
         mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
                 mExecutor);
+        mHotspotController.addCallback(mHotspotCallback);
+        mPolicyManager.registerListener(mPolicyListener);
         // Listen the subscription changes
         mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
         mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
@@ -340,6 +372,7 @@
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
         mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
         mConnectedWifiInternetMonitor.unregisterCallback();
+        mHotspotController.removeCallback(mHotspotCallback);
         mCallback = null;
     }
 
@@ -792,6 +825,12 @@
         startActivity(intent, view);
     }
 
+    void launchHotspotSetting(View view) {
+        final Intent intent = new Intent(Settings.ACTION_WIFI_TETHER_SETTING);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent, view);
+    }
+
     /**
      * Enable or disable Wi-Fi.
      *
@@ -1022,6 +1061,30 @@
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
     }
 
+    boolean isHotspotAvailable() {
+        return mHotspotController.isHotspotSupported();
+    }
+
+    boolean isHotspotEnabled() {
+        return mHotspotController.isHotspotEnabled();
+    }
+
+    boolean isHotspotTransient() {
+        return mHotspotController.isHotspotTransient();
+    }
+
+    int getHotspotNumDevices() {
+        return mHotspotController.getNumConnectedDevices();
+    }
+
+    void setHotspotEnabled(boolean enabled) {
+        mHotspotController.setHotspotEnabled(enabled);
+    }
+
+    boolean isDataSaverEnabled() {
+        return mPolicyManager.getRestrictBackground();
+    }
+
     boolean connect(WifiEntry ap) {
         if (ap == null) {
             if (DEBUG) {
@@ -1383,6 +1446,8 @@
                 @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
 
         void onWifiScan(boolean isScan);
+
+        void onHotspotChanged();
     }
 
     void makeOverlayToast(int stringId) {