diff options
8 files changed, 150 insertions, 24 deletions
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8d6b38f87801..edf19fd2dddc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -764,6 +764,11 @@ <string name="quick_settings_tethering_label">Tethering</string> <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] --> <string name="quick_settings_hotspot_label">Hotspot</string> + <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] --> + <plurals name="quick_settings_hotspot_num_devices"> + <item quantity="one">%d device</item> + <item quantity="other">%d devices</item> + </plurals> <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] --> <string name="quick_settings_notifications_label">Notifications</string> <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 910b6b174062..e1b58fe4c2d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -37,7 +38,7 @@ import com.android.systemui.statusbar.policy.HotspotController; /** Quick settings tile: Hotspot **/ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName( - "com.android.settings", "com.android.settings.TetherSettings")); + "com.android.settings", "com.android.settings.TetherSettings")); private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot); private final Icon mUnavailable = ResourceIcon.get(R.drawable.ic_hotspot_unavailable); @@ -115,11 +116,19 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { state.label = mContext.getString(R.string.quick_settings_hotspot_label); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING); - if (arg instanceof Boolean) { - state.value = (boolean) arg; + + final int numConnectedDevices; + if (arg instanceof CallbackInfo) { + CallbackInfo info = (CallbackInfo) arg; + state.value = info.enabled; + numConnectedDevices = info.numConnectedDevices; } else { state.value = mController.isHotspotEnabled(); + numConnectedDevices = mController.getNumConnectedDevices(); } + + state.secondaryLabel = getSecondaryLabel(state.value, numConnectedDevices); + state.icon = mEnabledStatic; state.isAirplaneMode = mAirplaneMode.getValue() != 0; state.isTransient = mController.isHotspotTransient(); @@ -133,6 +142,18 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { : state.value || state.isTransient ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; } + @Nullable + private String getSecondaryLabel(boolean enabled, int numConnectedDevices) { + if (numConnectedDevices > 0 && enabled) { + return mContext.getResources().getQuantityString( + R.plurals.quick_settings_hotspot_num_devices, + numConnectedDevices, + numConnectedDevices); + } + + return null; + } + @Override public int getMetricsCategory() { return MetricsEvent.QS_HOTSPOT; @@ -148,9 +169,30 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { } private final class Callback implements HotspotController.Callback { + final CallbackInfo mCallbackInfo = new CallbackInfo(); + @Override - public void onHotspotChanged(boolean enabled) { - refreshState(enabled); + public void onHotspotChanged(boolean enabled, int numConnectedDevices) { + mCallbackInfo.enabled = enabled; + mCallbackInfo.numConnectedDevices = numConnectedDevices; + refreshState(mCallbackInfo); } - }; + } + + /** + * Holder for any hotspot state info that needs to passed from the callback to + * {@link #handleUpdateState(State, Object)}. + */ + protected static final class CallbackInfo { + boolean enabled; + int numConnectedDevices; + + @Override + public String toString() { + return new StringBuilder("CallbackInfo[") + .append("enabled=").append(enabled) + .append(",numConnectedDevices=").append(numConnectedDevices) + .append(']').toString(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 149ec0b3a56a..36f9f6b79417 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -127,7 +127,7 @@ public class AutoTileManager { private final HotspotController.Callback mHotspotCallback = new Callback() { @Override - public void onHotspotChanged(boolean enabled) { + public void onHotspotChanged(boolean enabled, int numDevices) { if (mAutoTracker.isAdded(HOTSPOT)) return; if (enabled) { mHost.addTile(HOTSPOT); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6857337e5c10..20b501821a97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -665,7 +665,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() { @Override - public void onHotspotChanged(boolean enabled) { + public void onHotspotChanged(boolean enabled, int numDevices) { mIconController.setIconVisibility(mSlotHotspot, enabled); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java index 6457209a407b..830b50e35490 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java @@ -26,7 +26,9 @@ public interface HotspotController extends CallbackController<Callback>, Dumpabl void setHotspotEnabled(boolean enabled); boolean isHotspotSupported(); - public interface Callback { - void onHotspotChanged(boolean enabled); + int getNumConnectedDevices(); + + interface Callback { + void onHotspotChanged(boolean enabled, int numDevices); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 1ebb986e4488..8792b4f3cc4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -23,31 +23,35 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; -import android.os.Handler; import android.os.UserManager; import android.util.Log; +import com.android.systemui.Dependency; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -public class HotspotControllerImpl implements HotspotController { +public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback { private static final String TAG = "HotspotController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); - private final Receiver mReceiver = new Receiver(); + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver(); private final ConnectivityManager mConnectivityManager; + private final WifiManager mWifiManager; private final Context mContext; private int mHotspotState; + private int mNumConnectedDevices; private boolean mWaitingForCallback; public HotspotControllerImpl(Context context) { mContext = context; - mConnectivityManager = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); + mConnectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); } @Override @@ -84,7 +88,8 @@ public class HotspotControllerImpl implements HotspotController { if (callback == null || mCallbacks.contains(callback)) return; if (DEBUG) Log.d(TAG, "addCallback " + callback); mCallbacks.add(callback); - mReceiver.setListening(!mCallbacks.isEmpty()); + + updateWifiStateListeners(!mCallbacks.isEmpty()); } } @@ -94,7 +99,26 @@ public class HotspotControllerImpl implements HotspotController { if (DEBUG) Log.d(TAG, "removeCallback " + callback); synchronized (mCallbacks) { mCallbacks.remove(callback); - mReceiver.setListening(!mCallbacks.isEmpty()); + + updateWifiStateListeners(!mCallbacks.isEmpty()); + } + } + + /** + * Updates the wifi state receiver to either start or stop listening to get updates to the + * hotspot status. Additionally starts listening to wifi manager state to track the number of + * connected devices. + * + * @param shouldListen whether we should start listening to various wifi statuses + */ + private void updateWifiStateListeners(boolean shouldListen) { + mWifiStateReceiver.setListening(shouldListen); + if (shouldListen) { + mWifiManager.registerSoftApCallback( + this, + Dependency.get(Dependency.MAIN_HANDLER)); + } else { + mWifiManager.unregisterSoftApCallback(this); } } @@ -116,20 +140,55 @@ public class HotspotControllerImpl implements HotspotController { if (DEBUG) Log.d(TAG, "Starting tethering"); mConnectivityManager.startTethering( ConnectivityManager.TETHERING_WIFI, false, callback); - fireCallback(isHotspotEnabled()); + fireHotspotChangedCallback(isHotspotEnabled()); } else { mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); } } - private void fireCallback(boolean isEnabled) { + @Override + public int getNumConnectedDevices() { + return mNumConnectedDevices; + } + + /** + * Sends a hotspot changed callback with the new enabled status. Wraps + * {@link #fireHotspotChangedCallback(boolean, int)} and assumes that the number of devices has + * not changed. + * + * @param enabled whether the hotspot is enabled + */ + private void fireHotspotChangedCallback(boolean enabled) { + fireHotspotChangedCallback(enabled, mNumConnectedDevices); + } + + /** + * Sends a hotspot changed callback with the new enabled status & the number of devices + * connected to the hotspot. Be careful when calling over multiple threads, especially if one of + * them is the main thread (as it can be blocked). + * + * @param enabled whether the hotspot is enabled + * @param numConnectedDevices number of devices connected to the hotspot + */ + private void fireHotspotChangedCallback(boolean enabled, int numConnectedDevices) { synchronized (mCallbacks) { for (Callback callback : mCallbacks) { - callback.onHotspotChanged(isEnabled); + callback.onHotspotChanged(enabled, numConnectedDevices); } } } + @Override + public void onStateChanged(int state, int failureReason) { + // Do nothing - we don't care about changing anything here. + } + + @Override + public void onNumClientsChanged(int numConnectedDevices) { + mNumConnectedDevices = numConnectedDevices; + fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices); + } + private final class OnStartTetheringCallback extends ConnectivityManager.OnStartTetheringCallback { @Override @@ -143,12 +202,15 @@ public class HotspotControllerImpl implements HotspotController { public void onTetheringFailed() { if (DEBUG) Log.d(TAG, "onTetheringFailed"); mWaitingForCallback = false; - fireCallback(isHotspotEnabled()); + fireHotspotChangedCallback(isHotspotEnabled()); // TODO: Show error. } } - private final class Receiver extends BroadcastReceiver { + /** + * Class to listen in on wifi state and update the hotspot state + */ + private final class WifiStateReceiver extends BroadcastReceiver { private boolean mRegistered; public void setListening(boolean listening) { @@ -170,8 +232,17 @@ public class HotspotControllerImpl implements HotspotController { int state = intent.getIntExtra( WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED); if (DEBUG) Log.d(TAG, "onReceive " + state); + + // Update internal hotspot state for tracking before using any enabled/callback methods. mHotspotState = state; - fireCallback(mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED); + + if (!isHotspotEnabled()) { + // Reset num devices if the hotspot is no longer enabled so we don't get ghost + // counters. + mNumConnectedDevices = 0; + } + + fireHotspotChangedCallback(isHotspotEnabled()); } } } diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 859dc2f18dc6..f5e079ca9d61 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -47,6 +47,7 @@ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> + <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java index 54911473fc1c..016160aea433 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java @@ -44,4 +44,9 @@ public class FakeHotspotController extends BaseLeakChecker<Callback> implements public boolean isHotspotSupported() { return false; } + + @Override + public int getNumConnectedDevices() { + return 0; + } } |