Loading res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -2098,6 +2098,13 @@ <!-- The footer message for Wi-Fi hotspot security settings [CHAR LIMIT=NONE] --> <string name="wifi_hotspot_security_footer">Security settings may change if you change the hotspot’s frequency</string> <!-- Title for the instant hotspot state [CHAR LIMIT=NONE]--> <string name="wifi_hotspot_instant_title">Instant hotspot</string> <!-- Summary text when instant hotspot is turned on --> <string name="wifi_hotspot_instant_summary_on">On</string> <!-- Summary text when instant hotspot is turned off --> <string name="wifi_hotspot_instant_summary_off">Off</string> <!-- Summary text when turning hotspot on --> <string name="wifi_tether_starting">Turning hotspot on\u2026</string> <!-- Summary text when turning hotspot off --> Loading res/xml/wifi_tether_settings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -59,4 +59,10 @@ android:summary="@string/summary_placeholder" android:fragment="com.android.settings.wifi.tether.WifiHotspotSpeedSettings" settings:isPreferenceVisible="@bool/config_show_wifi_hotspot_speed"/> <Preference android:key="wifi_hotspot_instant" android:title="@string/wifi_hotspot_instant_title" android:summary="@string/summary_placeholder" settings:isPreferenceVisible="false"/> </PreferenceScreen> src/com/android/settings/wifi/factory/WifiFeatureProvider.java +13 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import com.android.settings.wifi.repository.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel; import com.android.settings.wifi.tether.WifiHotspotSpeedViewModel; Loading @@ -44,6 +45,7 @@ public class WifiFeatureProvider { private TetheringManager mTetheringManager; private WifiVerboseLogging mWifiVerboseLogging; private WifiHotspotRepository mWifiHotspotRepository; private SharedConnectivityRepository mSharedConnectivityRepository; public WifiFeatureProvider(@NonNull Context appContext) { mAppContext = appContext; Loading Loading @@ -92,6 +94,17 @@ public class WifiFeatureProvider { return mWifiHotspotRepository; } /** * Gets SharedConnectivityRepository */ public SharedConnectivityRepository getSharedConnectivityRepository() { if (mSharedConnectivityRepository == null) { mSharedConnectivityRepository = new SharedConnectivityRepository(mAppContext); verboseLog(TAG, "getSharedConnectivityRepository():" + mSharedConnectivityRepository); } return mSharedConnectivityRepository; } /** * Gets WifiTetherViewModel */ Loading src/com/android/settings/wifi/repository/SharedConnectivityRepository.java 0 → 100644 +184 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.wifi.repository; import android.app.PendingIntent; import android.content.Context; import android.net.wifi.sharedconnectivity.app.HotspotNetwork; import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; import android.net.wifi.sharedconnectivity.app.KnownNetwork; import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; import android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback; import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; import android.os.HandlerThread; import android.provider.DeviceConfig; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.android.settings.overlay.FeatureFactory; import java.util.List; import java.util.concurrent.Executor; /** * Shared Connectivity Repository for {@link SharedConnectivityManager} */ public class SharedConnectivityRepository { private static final String TAG = "SharedConnectivityRepository"; private static final String DEVICE_CONFIG_NAMESPACE = "wifi"; private static final String DEVICE_CONFIG_KEY = "shared_connectivity_enabled"; private Context mAppContext; private SharedConnectivityManager mManager; private ClientCallback mClientCallback = new ClientCallback(); private HandlerThread mWorkerThread = new HandlerThread(TAG); private Executor mWorkerExecutor = cmd -> mWorkerThread.getThreadHandler().post(cmd); private Runnable mLaunchSettingsRunnable = () -> handleLaunchSettings(); @VisibleForTesting MutableLiveData<SharedConnectivitySettingsState> mSettingsState = new MutableLiveData<>(); public SharedConnectivityRepository(@NonNull Context appContext) { this(appContext, DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false)); } @VisibleForTesting SharedConnectivityRepository(@NonNull Context appContext, boolean isConfigEnabled) { mAppContext = appContext; if (!isConfigEnabled) { return; } mManager = mAppContext.getSystemService(SharedConnectivityManager.class); if (mManager == null) { Log.w(TAG, "Failed to get SharedConnectivityManager"); return; } mWorkerThread.start(); mManager.registerCallback(mWorkerExecutor, mClientCallback); } /** * Return whether Wi-Fi Shared Connectivity service is available or not. * * @return {@code true} if Wi-Fi Shared Connectivity service is available */ public boolean isServiceAvailable() { return mManager != null; } /** * Gets SharedConnectivitySettingsState LiveData */ public LiveData<SharedConnectivitySettingsState> getSettingsState() { return mSettingsState; } /** * Launch Instant Hotspot Settings */ public void launchSettings() { mWorkerExecutor.execute(mLaunchSettingsRunnable); } @WorkerThread @VisibleForTesting void handleLaunchSettings() { if (mManager == null) { return; } SharedConnectivitySettingsState state = mManager.getSettingsState(); log("handleLaunchSettings(), state:" + state); if (state == null) { Log.e(TAG, "No SettingsState to launch Instant Hotspot settings"); return; } PendingIntent intent = state.getInstantTetherSettingsPendingIntent(); if (intent == null) { Log.e(TAG, "No PendingIntent to launch Instant Hotspot settings"); return; } sendSettingsIntent(intent); } @WorkerThread @VisibleForTesting void sendSettingsIntent(@NonNull PendingIntent intent) { try { log("sendSettingsIntent(), sent intent:" + intent); intent.send(); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Failed to launch Instant Hotspot settings", e); } } @WorkerThread class ClientCallback implements SharedConnectivityClientCallback { @Override public void onHotspotNetworkConnectionStatusChanged(HotspotNetworkConnectionStatus status) { log("onHotspotNetworkConnectionStatusChanged(), status:" + status); } @Override public void onHotspotNetworksUpdated(List<HotspotNetwork> networks) { log("onHotspotNetworksUpdated(), networks:" + networks); } @Override public void onKnownNetworkConnectionStatusChanged(KnownNetworkConnectionStatus status) { log("onKnownNetworkConnectionStatusChanged(), status:" + status); } @Override public void onKnownNetworksUpdated(List<KnownNetwork> networks) { log("onKnownNetworksUpdated(), networks:" + networks); } @Override public void onRegisterCallbackFailed(Exception e) { Log.e(TAG, "onRegisterCallbackFailed(), e:" + e); } @Override public void onServiceConnected() { SharedConnectivitySettingsState state = mManager.getSettingsState(); Log.d(TAG, "onServiceConnected(), Manager#getSettingsState:" + state); mSettingsState.postValue(state); } @Override public void onServiceDisconnected() { log("onServiceDisconnected()"); } @Override public void onSharedConnectivitySettingsChanged(SharedConnectivitySettingsState state) { Log.d(TAG, "onSharedConnectivitySettingsChanged(), state:" + state); mSettingsState.postValue(state); } } private void log(String msg) { FeatureFactory.getFactory(mAppContext).getWifiFeatureProvider().verboseLog(TAG, msg); } } src/com/android/settings/wifi/tether/WifiTetherSettings.java +33 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment static final String KEY_WIFI_HOTSPOT_SECURITY = "wifi_hotspot_security"; @VisibleForTesting static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed"; @VisibleForTesting static final String KEY_INSTANT_HOTSPOT = "wifi_hotspot_instant"; @VisibleForTesting SettingsMainSwitchBar mMainSwitchBar; Loading Loading @@ -103,6 +105,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment Preference mWifiHotspotSecurity; @VisibleForTesting Preference mWifiHotspotSpeed; @VisibleForTesting Preference mInstantHotspot; static { TETHER_STATE_CHANGE_FILTER = new IntentFilter(WIFI_AP_STATE_CHANGED_ACTION); Loading Loading @@ -148,6 +152,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment .getWifiTetherViewModel(this); if (mWifiTetherViewModel != null) { setupSpeedFeature(mWifiTetherViewModel.isSpeedFeatureAvailable()); setupInstantHotspot(mWifiTetherViewModel.isInstantHotspotFeatureAvailable()); mWifiTetherViewModel.getRestarting().observe(this, this::onRestartingChanged); } } Loading @@ -167,6 +172,24 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } } @VisibleForTesting void setupInstantHotspot(boolean isFeatureAvailable) { if (!isFeatureAvailable) { return; } mInstantHotspot = findPreference(KEY_INSTANT_HOTSPOT); if (mInstantHotspot == null) { Log.e(TAG, "Failed to find Instant Hotspot preference:" + KEY_INSTANT_HOTSPOT); return; } mWifiTetherViewModel.getInstantHotspotSummary() .observe(this, this::onInstantHotspotChanged); mInstantHotspot.setOnPreferenceClickListener(p -> { mWifiTetherViewModel.launchInstantHotspotSettings(); return true; }); } @Override public void onAttach(Context context) { super.onAttach(context); Loading Loading @@ -278,6 +301,16 @@ public class WifiTetherSettings extends RestrictedDashboardFragment setLoading(restarting, false); } @VisibleForTesting void onInstantHotspotChanged(String summary) { if (summary == null) { mInstantHotspot.setVisible(false); return; } mInstantHotspot.setVisible(true); mInstantHotspot.setSummary(summary); } @VisibleForTesting SoftApConfiguration buildNewConfig() { SoftApConfiguration currentConfig = mWifiTetherViewModel.getSoftApConfiguration(); Loading Loading
res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -2098,6 +2098,13 @@ <!-- The footer message for Wi-Fi hotspot security settings [CHAR LIMIT=NONE] --> <string name="wifi_hotspot_security_footer">Security settings may change if you change the hotspot’s frequency</string> <!-- Title for the instant hotspot state [CHAR LIMIT=NONE]--> <string name="wifi_hotspot_instant_title">Instant hotspot</string> <!-- Summary text when instant hotspot is turned on --> <string name="wifi_hotspot_instant_summary_on">On</string> <!-- Summary text when instant hotspot is turned off --> <string name="wifi_hotspot_instant_summary_off">Off</string> <!-- Summary text when turning hotspot on --> <string name="wifi_tether_starting">Turning hotspot on\u2026</string> <!-- Summary text when turning hotspot off --> Loading
res/xml/wifi_tether_settings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -59,4 +59,10 @@ android:summary="@string/summary_placeholder" android:fragment="com.android.settings.wifi.tether.WifiHotspotSpeedSettings" settings:isPreferenceVisible="@bool/config_show_wifi_hotspot_speed"/> <Preference android:key="wifi_hotspot_instant" android:title="@string/wifi_hotspot_instant_title" android:summary="@string/summary_placeholder" settings:isPreferenceVisible="false"/> </PreferenceScreen>
src/com/android/settings/wifi/factory/WifiFeatureProvider.java +13 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import com.android.settings.wifi.repository.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel; import com.android.settings.wifi.tether.WifiHotspotSpeedViewModel; Loading @@ -44,6 +45,7 @@ public class WifiFeatureProvider { private TetheringManager mTetheringManager; private WifiVerboseLogging mWifiVerboseLogging; private WifiHotspotRepository mWifiHotspotRepository; private SharedConnectivityRepository mSharedConnectivityRepository; public WifiFeatureProvider(@NonNull Context appContext) { mAppContext = appContext; Loading Loading @@ -92,6 +94,17 @@ public class WifiFeatureProvider { return mWifiHotspotRepository; } /** * Gets SharedConnectivityRepository */ public SharedConnectivityRepository getSharedConnectivityRepository() { if (mSharedConnectivityRepository == null) { mSharedConnectivityRepository = new SharedConnectivityRepository(mAppContext); verboseLog(TAG, "getSharedConnectivityRepository():" + mSharedConnectivityRepository); } return mSharedConnectivityRepository; } /** * Gets WifiTetherViewModel */ Loading
src/com/android/settings/wifi/repository/SharedConnectivityRepository.java 0 → 100644 +184 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.wifi.repository; import android.app.PendingIntent; import android.content.Context; import android.net.wifi.sharedconnectivity.app.HotspotNetwork; import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; import android.net.wifi.sharedconnectivity.app.KnownNetwork; import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; import android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback; import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; import android.os.HandlerThread; import android.provider.DeviceConfig; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.android.settings.overlay.FeatureFactory; import java.util.List; import java.util.concurrent.Executor; /** * Shared Connectivity Repository for {@link SharedConnectivityManager} */ public class SharedConnectivityRepository { private static final String TAG = "SharedConnectivityRepository"; private static final String DEVICE_CONFIG_NAMESPACE = "wifi"; private static final String DEVICE_CONFIG_KEY = "shared_connectivity_enabled"; private Context mAppContext; private SharedConnectivityManager mManager; private ClientCallback mClientCallback = new ClientCallback(); private HandlerThread mWorkerThread = new HandlerThread(TAG); private Executor mWorkerExecutor = cmd -> mWorkerThread.getThreadHandler().post(cmd); private Runnable mLaunchSettingsRunnable = () -> handleLaunchSettings(); @VisibleForTesting MutableLiveData<SharedConnectivitySettingsState> mSettingsState = new MutableLiveData<>(); public SharedConnectivityRepository(@NonNull Context appContext) { this(appContext, DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false)); } @VisibleForTesting SharedConnectivityRepository(@NonNull Context appContext, boolean isConfigEnabled) { mAppContext = appContext; if (!isConfigEnabled) { return; } mManager = mAppContext.getSystemService(SharedConnectivityManager.class); if (mManager == null) { Log.w(TAG, "Failed to get SharedConnectivityManager"); return; } mWorkerThread.start(); mManager.registerCallback(mWorkerExecutor, mClientCallback); } /** * Return whether Wi-Fi Shared Connectivity service is available or not. * * @return {@code true} if Wi-Fi Shared Connectivity service is available */ public boolean isServiceAvailable() { return mManager != null; } /** * Gets SharedConnectivitySettingsState LiveData */ public LiveData<SharedConnectivitySettingsState> getSettingsState() { return mSettingsState; } /** * Launch Instant Hotspot Settings */ public void launchSettings() { mWorkerExecutor.execute(mLaunchSettingsRunnable); } @WorkerThread @VisibleForTesting void handleLaunchSettings() { if (mManager == null) { return; } SharedConnectivitySettingsState state = mManager.getSettingsState(); log("handleLaunchSettings(), state:" + state); if (state == null) { Log.e(TAG, "No SettingsState to launch Instant Hotspot settings"); return; } PendingIntent intent = state.getInstantTetherSettingsPendingIntent(); if (intent == null) { Log.e(TAG, "No PendingIntent to launch Instant Hotspot settings"); return; } sendSettingsIntent(intent); } @WorkerThread @VisibleForTesting void sendSettingsIntent(@NonNull PendingIntent intent) { try { log("sendSettingsIntent(), sent intent:" + intent); intent.send(); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Failed to launch Instant Hotspot settings", e); } } @WorkerThread class ClientCallback implements SharedConnectivityClientCallback { @Override public void onHotspotNetworkConnectionStatusChanged(HotspotNetworkConnectionStatus status) { log("onHotspotNetworkConnectionStatusChanged(), status:" + status); } @Override public void onHotspotNetworksUpdated(List<HotspotNetwork> networks) { log("onHotspotNetworksUpdated(), networks:" + networks); } @Override public void onKnownNetworkConnectionStatusChanged(KnownNetworkConnectionStatus status) { log("onKnownNetworkConnectionStatusChanged(), status:" + status); } @Override public void onKnownNetworksUpdated(List<KnownNetwork> networks) { log("onKnownNetworksUpdated(), networks:" + networks); } @Override public void onRegisterCallbackFailed(Exception e) { Log.e(TAG, "onRegisterCallbackFailed(), e:" + e); } @Override public void onServiceConnected() { SharedConnectivitySettingsState state = mManager.getSettingsState(); Log.d(TAG, "onServiceConnected(), Manager#getSettingsState:" + state); mSettingsState.postValue(state); } @Override public void onServiceDisconnected() { log("onServiceDisconnected()"); } @Override public void onSharedConnectivitySettingsChanged(SharedConnectivitySettingsState state) { Log.d(TAG, "onSharedConnectivitySettingsChanged(), state:" + state); mSettingsState.postValue(state); } } private void log(String msg) { FeatureFactory.getFactory(mAppContext).getWifiFeatureProvider().verboseLog(TAG, msg); } }
src/com/android/settings/wifi/tether/WifiTetherSettings.java +33 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment static final String KEY_WIFI_HOTSPOT_SECURITY = "wifi_hotspot_security"; @VisibleForTesting static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed"; @VisibleForTesting static final String KEY_INSTANT_HOTSPOT = "wifi_hotspot_instant"; @VisibleForTesting SettingsMainSwitchBar mMainSwitchBar; Loading Loading @@ -103,6 +105,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment Preference mWifiHotspotSecurity; @VisibleForTesting Preference mWifiHotspotSpeed; @VisibleForTesting Preference mInstantHotspot; static { TETHER_STATE_CHANGE_FILTER = new IntentFilter(WIFI_AP_STATE_CHANGED_ACTION); Loading Loading @@ -148,6 +152,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment .getWifiTetherViewModel(this); if (mWifiTetherViewModel != null) { setupSpeedFeature(mWifiTetherViewModel.isSpeedFeatureAvailable()); setupInstantHotspot(mWifiTetherViewModel.isInstantHotspotFeatureAvailable()); mWifiTetherViewModel.getRestarting().observe(this, this::onRestartingChanged); } } Loading @@ -167,6 +172,24 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } } @VisibleForTesting void setupInstantHotspot(boolean isFeatureAvailable) { if (!isFeatureAvailable) { return; } mInstantHotspot = findPreference(KEY_INSTANT_HOTSPOT); if (mInstantHotspot == null) { Log.e(TAG, "Failed to find Instant Hotspot preference:" + KEY_INSTANT_HOTSPOT); return; } mWifiTetherViewModel.getInstantHotspotSummary() .observe(this, this::onInstantHotspotChanged); mInstantHotspot.setOnPreferenceClickListener(p -> { mWifiTetherViewModel.launchInstantHotspotSettings(); return true; }); } @Override public void onAttach(Context context) { super.onAttach(context); Loading Loading @@ -278,6 +301,16 @@ public class WifiTetherSettings extends RestrictedDashboardFragment setLoading(restarting, false); } @VisibleForTesting void onInstantHotspotChanged(String summary) { if (summary == null) { mInstantHotspot.setVisible(false); return; } mInstantHotspot.setVisible(true); mInstantHotspot.setSummary(summary); } @VisibleForTesting SoftApConfiguration buildNewConfig() { SoftApConfiguration currentConfig = mWifiTetherViewModel.getSoftApConfiguration(); Loading