Merge changes Ib975d5a7,Idcc93f9e,Ibc0f30d4,I5b09ec1d
* changes:
Check whether a Wi-Fi network is trusted.
Introduce communal trusted network condition.
Introduce communal setting condition.
Introduction to communal conditions monitor.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index 2244532..d3018e3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,17 +16,11 @@
package com.android.systemui.communal;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
-import androidx.annotation.MainThread;
-
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.util.settings.SecureSettings;
import com.google.android.collect.Lists;
@@ -46,10 +40,15 @@
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final SecureSettings mSecureSettings;
+ private final CommunalConditionsMonitor mConditionsMonitor;
private CommunalSource mCurrentSource;
- private boolean mCommunalEnabled;
+
+ // Whether all conditions for communal mode to show have been met.
+ private boolean mAllCommunalConditionsMet = false;
+
+ // Whether the class is currently listening for condition changes.
+ private boolean mListeningForConditions = false;
private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
@Override
@@ -59,24 +58,20 @@
}
};
+ private final CommunalConditionsMonitor.Callback mConditionsCallback =
+ allConditionsMet -> {
+ if (mAllCommunalConditionsMet != allConditionsMet) {
+ if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
+
+ mAllCommunalConditionsMet = allConditionsMet;
+ executeOnSourceAvailableCallbacks();
+ }
+ };
+
@VisibleForTesting
@Inject
- public CommunalSourceMonitor(
- @MainThread Handler mainThreadHandler,
- SecureSettings secureSettings) {
- mSecureSettings = secureSettings;
-
- ContentObserver settingsObserver = new ContentObserver(mainThreadHandler) {
- @Override
- public void onChange(boolean selfChange) {
- reloadSettings();
- }
- };
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- /* notifyForDescendants= */false,
- settingsObserver, UserHandle.USER_SYSTEM);
- reloadSettings();
+ public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
+ mConditionsMonitor = communalConditionsMonitor;
}
/**
@@ -92,7 +87,7 @@
mCurrentSource = source;
- if (mCommunalEnabled) {
+ if (mAllCommunalConditionsMet) {
executeOnSourceAvailableCallbacks();
}
@@ -111,7 +106,7 @@
itr.remove();
} else {
cb.onSourceAvailable(
- (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>(
+ (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
mCurrentSource) : null);
}
}
@@ -126,9 +121,14 @@
mCallbacks.add(new WeakReference<>(callback));
// Inform the callback of any already present CommunalSource.
- if (mCommunalEnabled && mCurrentSource != null) {
+ if (mAllCommunalConditionsMet && mCurrentSource != null) {
callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
}
+
+ if (!mListeningForConditions) {
+ mConditionsMonitor.addCallback(mConditionsCallback);
+ mListeningForConditions = true;
+ }
}
/**
@@ -138,21 +138,10 @@
*/
public void removeCallback(Callback callback) {
mCallbacks.removeIf(el -> el.get() == callback);
- }
- private void reloadSettings() {
- boolean newCommunalEnabled = mSecureSettings.getIntForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- 1,
- UserHandle.USER_SYSTEM) == 1;
-
- if (DEBUG) {
- Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled);
- }
-
- if (mCommunalEnabled != newCommunalEnabled) {
- mCommunalEnabled = newCommunalEnabled;
- executeOnSourceAvailableCallbacks();
+ if (mCallbacks.isEmpty() && mListeningForConditions) {
+ mConditionsMonitor.removeCallback(mConditionsCallback);
+ mListeningForConditions = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
new file mode 100644
index 0000000..734ab63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
@@ -0,0 +1,130 @@
+/*
+ * 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.communal.conditions;
+
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Base class for a condition that needs to be fulfilled in order for Communal Mode to display.
+ */
+public abstract class CommunalCondition implements CallbackController<CommunalCondition.Callback> {
+ private final String mTag = getClass().getSimpleName();
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private boolean mIsConditionMet = false;
+ private boolean mStarted = false;
+
+ /**
+ * Starts monitoring the condition.
+ */
+ protected abstract void start();
+
+ /**
+ * Stops monitoring the condition.
+ */
+ protected abstract void stop();
+
+ /**
+ * Registers a callback to receive updates once started. This should be called before
+ * {@link #start()}. Also triggers the callback immediately if already started.
+ */
+ @Override
+ public void addCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "adding callback");
+ mCallbacks.add(new WeakReference<>(callback));
+
+ if (mStarted) {
+ callback.onConditionChanged(this, mIsConditionMet);
+ return;
+ }
+
+ start();
+ mStarted = true;
+ }
+
+ /**
+ * Removes the provided callback from further receiving updates.
+ */
+ @Override
+ public void removeCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
+
+ if (!mCallbacks.isEmpty() || !mStarted) {
+ return;
+ }
+
+ stop();
+ mStarted = false;
+ }
+
+ /**
+ * Updates the value for whether the condition has been fulfilled, and sends an update if the
+ * value changes and any callback is registered.
+ *
+ * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+ */
+ protected void updateCondition(boolean isConditionMet) {
+ if (mIsConditionMet == isConditionMet) {
+ return;
+ }
+
+ if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet);
+ mIsConditionMet = isConditionMet;
+
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null) {
+ iterator.remove();
+ } else {
+ cb.onConditionChanged(this, mIsConditionMet);
+ }
+ }
+ }
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+
+ /**
+ * Callback that receives updates about whether the condition has been fulfilled.
+ */
+ public interface Callback {
+ /**
+ * Called when the fulfillment of the condition changes.
+ *
+ * @param condition The condition in question.
+ * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+ */
+ void onConditionChanged(CommunalCondition condition, boolean isConditionMet);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
new file mode 100644
index 0000000..4f772da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
@@ -0,0 +1,154 @@
+/*
+ * 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.communal.conditions;
+
+import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link CommunalConditionsMonitor} takes in a set of conditions, monitors whether all of them have
+ * been fulfilled, and informs any registered listeners.
+ */
+@SysUISingleton
+public class CommunalConditionsMonitor implements
+ CallbackController<CommunalConditionsMonitor.Callback> {
+ private final String mTag = getClass().getSimpleName();
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+
+ // Set of all conditions that need to be monitored.
+ private final Set<CommunalCondition> mConditions;
+
+ // Map of values of each condition.
+ private final HashMap<CommunalCondition, Boolean> mConditionsMap = new HashMap<>();
+
+ // Whether all conditions have been met.
+ private boolean mAllConditionsMet = false;
+
+ // Whether the monitor has started listening for all the conditions.
+ private boolean mHaveConditionsStarted = false;
+
+ // Callback for when each condition has been updated.
+ private final CommunalCondition.Callback mConditionCallback = (condition, isConditionMet) -> {
+ mConditionsMap.put(condition, isConditionMet);
+
+ final boolean newAllConditionsMet = !mConditionsMap.containsValue(false);
+
+ if (newAllConditionsMet == mAllConditionsMet) {
+ return;
+ }
+
+ if (shouldLog()) Log.d(mTag, "all conditions met: " + newAllConditionsMet);
+ mAllConditionsMet = newAllConditionsMet;
+
+ // Updates all callbacks.
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback callback = iterator.next().get();
+ if (callback == null) {
+ iterator.remove();
+ } else {
+ callback.onConditionsChanged(mAllConditionsMet);
+ }
+ }
+ };
+
+ @Inject
+ public CommunalConditionsMonitor(
+ @Named(COMMUNAL_CONDITIONS) Set<CommunalCondition> communalConditions) {
+ mConditions = communalConditions;
+
+ // Initializes the conditions map and registers a callback for each condition.
+ mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+ }
+
+ @Override
+ public void addCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "adding callback");
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // Updates the callback immediately.
+ callback.onConditionsChanged(mAllConditionsMet);
+
+ if (!mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "starting all conditions");
+ mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
+ mHaveConditionsStarted = true;
+ }
+ }
+
+ @Override
+ public void removeCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
+
+ if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "stopping all conditions");
+ mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+
+ mAllConditionsMet = false;
+ mHaveConditionsStarted = false;
+ }
+ }
+
+ /**
+ * Force updates each condition to the value provided.
+ */
+ @VisibleForTesting
+ public void overrideAllConditionsMet(boolean value) {
+ mConditions.forEach(condition -> condition.updateCondition(value));
+ }
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+
+ /**
+ * Callback that receives updates of whether all conditions have been fulfilled.
+ */
+ public interface Callback {
+ /**
+ * Triggered when the fulfillment of all conditions have been met.
+ *
+ * @param allConditionsMet True if all conditions have been fulfilled. False if none or
+ * only partial conditions have been fulfilled.
+ */
+ void onConditionsChanged(boolean allConditionsMet);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
new file mode 100644
index 0000000..1616b18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
@@ -0,0 +1,66 @@
+/*
+ * 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.communal.conditions;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.MainThread;
+
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/**
+ * Monitors the communal setting, and informs any listeners with updates.
+ */
+public class CommunalSettingCondition extends CommunalCondition {
+ private final SecureSettings mSecureSettings;
+ private final ContentObserver mCommunalSettingContentObserver;
+
+ @Inject
+ public CommunalSettingCondition(@MainThread Handler mainHandler,
+ SecureSettings secureSettings) {
+ mSecureSettings = secureSettings;
+
+ mCommunalSettingContentObserver = new ContentObserver(mainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final boolean communalSettingEnabled = mSecureSettings.getIntForUser(
+ Settings.Secure.COMMUNAL_MODE_ENABLED, 0, UserHandle.USER_SYSTEM) == 1;
+ updateCondition(communalSettingEnabled);
+ }
+ };
+ }
+
+ @Override
+ protected void start() {
+ mSecureSettings.registerContentObserverForUser(Settings.Secure.COMMUNAL_MODE_ENABLED,
+ false /*notifyForDescendants*/, mCommunalSettingContentObserver,
+ UserHandle.USER_SYSTEM);
+
+ // Fetches setting immediately.
+ mCommunalSettingContentObserver.onChange(false);
+ }
+
+ @Override
+ protected void stop() {
+ mSecureSettings.unregisterContentObserver(mCommunalSettingContentObserver);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
new file mode 100644
index 0000000..e4692db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
@@ -0,0 +1,189 @@
+/*
+ * 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.communal.conditions;
+
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiInfo;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import javax.inject.Inject;
+
+/**
+ * Monitors Wi-Fi connections and triggers callback, if any, when the device is connected to and
+ * disconnected from a trusted network.
+ */
+public class CommunalTrustedNetworkCondition extends CommunalCondition {
+ private final String mTag = getClass().getSimpleName();
+ private final ConnectivityManager mConnectivityManager;
+ private final ContentObserver mTrustedNetworksObserver;
+ private final SecureSettings mSecureSettings;
+
+ // The SSID of the connected Wi-Fi network. Null if not connected to Wi-Fi.
+ private String mWifiSSID;
+
+ // Set of SSIDs of trusted networks.
+ private final HashSet<String> mTrustedNetworks = new HashSet<>();
+
+ /**
+ * The deliminator used to separate trusted network keys saved as a string in secure settings.
+ */
+ public static final String SETTINGS_STRING_DELIMINATOR = ",/";
+
+ @Inject
+ public CommunalTrustedNetworkCondition(@Main Handler handler,
+ ConnectivityManager connectivityManager, SecureSettings secureSettings) {
+ mConnectivityManager = connectivityManager;
+ mSecureSettings = secureSettings;
+
+ mTrustedNetworksObserver = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ fetchTrustedNetworks();
+ }
+ };
+ }
+
+ /**
+ * Starts monitoring for trusted network connection. Ignores if already started.
+ */
+ @Override
+ protected void start() {
+ if (shouldLog()) Log.d(mTag, "start listening for wifi connections");
+
+ fetchTrustedNetworks();
+
+ final NetworkRequest wifiNetworkRequest = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI).build();
+ mConnectivityManager.registerNetworkCallback(wifiNetworkRequest, mNetworkCallback);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, false, mTrustedNetworksObserver,
+ UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * Stops monitoring for trusted network connection.
+ */
+ @Override
+ protected void stop() {
+ if (shouldLog()) Log.d(mTag, "stop listening for wifi connections");
+
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ mSecureSettings.unregisterContentObserver(mTrustedNetworksObserver);
+ }
+
+ private void updateWifiInfo(WifiInfo wifiInfo) {
+ if (wifiInfo == null) {
+ mWifiSSID = null;
+ } else {
+ // Remove the wrapping quotes around the SSID.
+ mWifiSSID = wifiInfo.getSSID().replace("\"", "");
+ }
+
+ checkIfConnectedToTrustedNetwork();
+ }
+
+ private void fetchTrustedNetworks() {
+ final String trustedNetworksString = mSecureSettings.getStringForUser(
+ Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, UserHandle.USER_SYSTEM);
+ mTrustedNetworks.clear();
+
+ if (shouldLog()) Log.d(mTag, "fetched trusted networks: " + trustedNetworksString);
+
+ if (TextUtils.isEmpty(trustedNetworksString)) {
+ return;
+ }
+
+ mTrustedNetworks.addAll(
+ Arrays.asList(trustedNetworksString.split(SETTINGS_STRING_DELIMINATOR)));
+
+ checkIfConnectedToTrustedNetwork();
+ }
+
+ private void checkIfConnectedToTrustedNetwork() {
+ final boolean connectedToTrustedNetwork = mWifiSSID != null && mTrustedNetworks.contains(
+ mWifiSSID);
+
+ if (shouldLog()) {
+ Log.d(mTag, (connectedToTrustedNetwork ? "connected to" : "disconnected from")
+ + " a trusted network");
+ }
+
+ updateCondition(connectedToTrustedNetwork);
+ }
+
+ private final ConnectivityManager.NetworkCallback mNetworkCallback =
+ new ConnectivityManager.NetworkCallback(
+ ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
+ private boolean mIsConnected = false;
+ private WifiInfo mWifiInfo;
+
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ super.onAvailable(network);
+
+ if (shouldLog()) Log.d(mTag, "connected to wifi");
+
+ mIsConnected = true;
+ if (mWifiInfo != null) {
+ updateWifiInfo(mWifiInfo);
+ }
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ super.onLost(network);
+
+ if (shouldLog()) Log.d(mTag, "disconnected from wifi");
+
+ mIsConnected = false;
+ mWifiInfo = null;
+ updateWifiInfo(null);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ super.onCapabilitiesChanged(network, networkCapabilities);
+
+ mWifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+
+ if (mIsConnected) {
+ updateWifiInfo(mWifiInfo);
+ }
+ }
+ };
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index 31dd82d..3ebfb51 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,15 +20,23 @@
import android.view.View;
import android.widget.FrameLayout;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
/**
* Dagger Module providing Communal-related functionality.
@@ -39,6 +47,7 @@
})
public interface CommunalModule {
String IDLE_VIEW = "idle_view";
+ String COMMUNAL_CONDITIONS = "communal_conditions";
/** */
@Provides
@@ -56,4 +65,17 @@
@Binds
AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
LightSensorEventsDebounceAlgorithm algorithm);
+
+ /**
+ * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
+ */
+ @Provides
+ @ElementsIntoSet
+ @Named(COMMUNAL_CONDITIONS)
+ static Set<CommunalCondition> provideCommunalConditions(
+ CommunalSettingCondition communalSettingCondition,
+ CommunalTrustedNetworkCondition communalTrustedNetworkCondition) {
+ return new HashSet<>(
+ Arrays.asList(communalSettingCondition, communalTrustedNetworkCondition));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
new file mode 100644
index 0000000..9d7ef0f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionTest extends SysuiTestCase {
+ private FakeCommunalCondition mCondition;
+
+ @Before
+ public void setup() {
+ mCondition = spy(new FakeCommunalCondition());
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_triggerStart() {
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(mCondition).start();
+ }
+
+ @Test
+ public void addCallback_addMultipleCallbacks_triggerStartOnlyOnce() {
+ final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+ final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+ final CommunalCondition.Callback callback3 = mock(CommunalCondition.Callback.class);
+
+ mCondition.addCallback(callback1);
+ mCondition.addCallback(callback2);
+ mCondition.addCallback(callback3);
+
+ verify(mCondition, times(1)).start();
+ }
+
+ @Test
+ public void addCallback_alreadyStarted_triggerUpdate() {
+ final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback1);
+
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback2);
+ verify(callback2).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void removeCallback_removeLastCallback_triggerStop() {
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(mCondition, never()).stop();
+
+ mCondition.removeCallback(callback);
+ verify(mCondition).stop();
+ }
+
+ @Test
+ public void updateCondition_falseToTrue_reportTrue() {
+ mCondition.fakeUpdateCondition(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(true);
+ verify(callback).onConditionChanged(eq(mCondition), eq(true));
+ }
+
+ @Test
+ public void updateCondition_trueToFalse_reportFalse() {
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(false);
+ verify(callback).onConditionChanged(eq(mCondition), eq(false));
+ }
+
+ @Test
+ public void updateCondition_trueToTrue_reportNothing() {
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCondition_falseToFalse_reportNothing() {
+ mCondition.fakeUpdateCondition(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(false);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
new file mode 100644
index 0000000..59ddba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.communal;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionsMonitorTest extends SysuiTestCase {
+ private FakeCommunalCondition mCondition1;
+ private FakeCommunalCondition mCondition2;
+ private FakeCommunalCondition mCondition3;
+ private HashSet<CommunalCondition> mConditions;
+
+ private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mCondition1 = spy(new FakeCommunalCondition());
+ mCondition2 = spy(new FakeCommunalCondition());
+ mCondition3 = spy(new FakeCommunalCondition());
+ mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
+
+ mCommunalConditionsMonitor = new CommunalConditionsMonitor(mConditions);
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_addCallbackToAllConditions() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+ mConditions.forEach(condition -> verify(condition).addCallback(any()));
+
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback2);
+ mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_reportWithDefaultValue() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ verify(callback).onConditionsChanged(false);
+ }
+
+ @Test
+ public void addCallback_addSecondCallback_reportWithExistingValue() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback2);
+ verify(callback2).onConditionsChanged(true);
+ }
+
+ @Test
+ public void removeCallback_shouldNoLongerReceiveUpdate() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ clearInvocations(callback);
+ mCommunalConditionsMonitor.removeCallback(callback);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+ verify(callback, never()).onConditionsChanged(true);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(false);
+ verify(callback, never()).onConditionsChanged(false);
+ }
+
+ @Test
+ public void removeCallback_removeLastCallback_removeCallbackFromAllConditions() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+ mCommunalConditionsMonitor.addCallback(callback2);
+
+ mCommunalConditionsMonitor.removeCallback(callback1);
+ mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
+
+ mCommunalConditionsMonitor.removeCallback(callback2);
+ mConditions.forEach(condition -> verify(condition).removeCallback(any()));
+ }
+
+ @Test
+ public void updateCallbacks_allConditionsMet_reportTrue() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
+ public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(false);
+ verify(callback).onConditionsChanged(false);
+ }
+
+ @Test
+ public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ verify(callback).onConditionsChanged(false);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ mCondition2.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ mCondition3.fakeUpdateCondition(true);
+ verify(callback).onConditionsChanged(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
new file mode 100644
index 0000000..cf147f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.utils.os.FakeHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSettingConditionTest extends SysuiTestCase {
+ private FakeSettings mSecureSettings;
+ private CommunalSettingCondition mCondition;
+
+ @Before
+ public void setup() {
+ final FakeHandler handler = new FakeHandler(Looper.getMainLooper());
+ mSecureSettings = new FakeSettings();
+ mCondition = new CommunalSettingCondition(handler, mSecureSettings);
+ }
+
+ @Test
+ public void addCallback_communalSettingEnabled_immediatelyReportsTrue() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void addCallback_communalSettingDisabled_noReport() {
+ updateCommunalSetting(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCallback_communalSettingEnabled_reportsTrue() {
+ updateCommunalSetting(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(true);
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void updateCallback_communalSettingDisabled_reportsFalse() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(false);
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ @Test
+ public void updateCallback_communalSettingDidNotChange_neverReportDup() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(true);
+ verify(callback, never()).onConditionChanged(mCondition, true);
+ }
+
+ private void updateCommunalSetting(boolean value) {
+ mSecureSettings.putIntForUser(Settings.Secure.COMMUNAL_MODE_ENABLED, value ? 1 : 0,
+ UserHandle.USER_SYSTEM);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index 68600de..df9a2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -20,25 +20,25 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.ref.WeakReference;
@@ -47,15 +47,16 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CommunalSourceMonitorTest extends SysuiTestCase {
+ @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+ @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+
private CommunalSourceMonitor mCommunalSourceMonitor;
- private FakeSettings mSecureSettings;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSecureSettings = new FakeSettings();
- mCommunalSourceMonitor = new CommunalSourceMonitor(
- Handler.createAsync(TestableLooper.get(this).getLooper()), mSecureSettings);
+ mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
}
@Test
@@ -65,6 +66,7 @@
mCommunalSourceMonitor.setSource(source);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
}
@@ -82,33 +84,32 @@
}
@Test
- public void testAddCallbackWithDefaultSetting() {
+ public void testAddCallbackWithConditionsMet() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(true);
+ clearInvocations(callback);
mCommunalSourceMonitor.setSource(source);
verifyOnSourceAvailableCalledWith(callback, source);
}
@Test
- public void testAddCallbackWithSettingDisabled() {
- setCommunalEnabled(false);
-
+ public void testAddCallbackWithConditionsNotMet() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(false);
mCommunalSourceMonitor.setSource(source);
verify(callback, never()).onSourceAvailable(any());
}
@Test
- public void testSettingEnabledAfterCallbackAdded() {
- setCommunalEnabled(false);
-
+ public void testConditionsAreMetAfterCallbackAdded() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
@@ -118,33 +119,27 @@
// The callback should not have executed since communal is disabled.
verify(callback, never()).onSourceAvailable(any());
- // The callback should execute when the user changes the setting to enabled.
- setCommunalEnabled(true);
+ // The callback should execute when all conditions are met.
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
}
@Test
- public void testSettingDisabledAfterCallbackAdded() {
+ public void testConditionsNoLongerMetAfterCallbackAdded() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
mCommunalSourceMonitor.setSource(source);
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
- // The callback should execute again when the setting is disabled, with a value of null.
- setCommunalEnabled(false);
+ // The callback should execute again when conditions are no longer met, with a value of
+ // null.
+ setConditionsMet(false);
verify(callback).onSourceAvailable(null);
}
- private void setCommunalEnabled(boolean enabled) {
- mSecureSettings.putIntForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- enabled ? 1 : 0,
- UserHandle.USER_SYSTEM);
- TestableLooper.get(this).processAllMessages();
- }
-
private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback,
CommunalSource source) {
final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
@@ -152,4 +147,13 @@
verify(callback).onSourceAvailable(sourceCapture.capture());
assertThat(sourceCapture.getValue().get()).isEqualTo(source);
}
+
+ // Pushes an update on whether the communal conditions are met, assuming that a callback has
+ // been registered with the communal conditions monitor.
+ private void setConditionsMet(boolean value) {
+ verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
+ final CommunalConditionsMonitor.Callback conditionsCallback =
+ mConditionsCallbackCaptor.getValue();
+ conditionsCallback.onConditionsChanged(value);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
new file mode 100644
index 0000000..61a5126
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.utils.os.FakeHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalTrustedNetworkConditionTest extends SysuiTestCase {
+ @Mock private ConnectivityManager mConnectivityManager;
+
+ @Captor private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor;
+
+ private final Handler mHandler = new FakeHandler(Looper.getMainLooper());
+ private CommunalTrustedNetworkCondition mCondition;
+
+ private final String mTrustedWifi1 = "wifi-1";
+ private final String mTrustedWifi2 = "wifi-2";
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ final FakeSettings secureSettings = new FakeSettings();
+ secureSettings.putStringForUser(Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS,
+ mTrustedWifi1 + CommunalTrustedNetworkCondition.SETTINGS_STRING_DELIMINATOR
+ + mTrustedWifi2, UserHandle.USER_SYSTEM);
+ mCondition = new CommunalTrustedNetworkCondition(mHandler, mConnectivityManager,
+ secureSettings);
+ }
+
+ @Test
+ public void updateCallback_connectedToTrustedNetwork_reportsTrue() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void updateCallback_switchedToAnotherTrustedNetwork_reportsNothing() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to a trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+ clearInvocations(callback);
+
+ // Connected to another trusted Wi-Fi network.
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
+
+ // Verifies that the callback is not triggered.
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCallback_connectedToNonTrustedNetwork_reportsFalse() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+
+ // Connected to non-trusted Wi-Fi network.
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ @Test
+ public void updateCallback_disconnectedFromNetwork_reportsFalse() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to Wi-Fi.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+ clearInvocations(callback);
+
+ // Disconnected from Wi-Fi.
+ networkCallback.onLost(network);
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ // Captures and returns the network callback, assuming it is registered with the connectivity
+ // manager.
+ private ConnectivityManager.NetworkCallback captureNetworkCallback() {
+ verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
+ mNetworkCallbackCaptor.capture());
+ return mNetworkCallbackCaptor.getValue();
+ }
+
+ private NetworkCapabilities fakeNetworkCapabilities(String ssid) {
+ final NetworkCapabilities networkCapabilities = mock(NetworkCapabilities.class);
+ final WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.getSSID()).thenReturn(ssid);
+ when(networkCapabilities.getTransportInfo()).thenReturn(wifiInfo);
+ return networkCapabilities;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
new file mode 100644
index 0000000..882effd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
@@ -0,0 +1,35 @@
+/*
+ * 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.communal;
+
+import com.android.systemui.communal.conditions.CommunalCondition;
+
+/**
+ * Fake implementation of {@link CommunalCondition}, and provides a way for tests to update
+ * condition fulfillment.
+ */
+public class FakeCommunalCondition extends CommunalCondition {
+ @Override
+ public void start() {}
+
+ @Override
+ public void stop() {}
+
+ public void fakeUpdateCondition(boolean isConditionMet) {
+ updateCondition(isConditionMet);
+ }
+}