diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java index 36fc5fac14e7a..03693301b5f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java @@ -16,8 +16,17 @@ 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.dagger.SysUISingleton; +import com.android.systemui.util.settings.SecureSettings; import com.google.android.collect.Lists; @@ -31,10 +40,15 @@ import javax.inject.Inject; */ @SysUISingleton public class CommunalSourceMonitor { + private static final String TAG = "CommunalSourceMonitor"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // A list of {@link Callback} that have registered to receive updates. private final ArrayList> mCallbacks = Lists.newArrayList(); + private final SecureSettings mSecureSettings; private CommunalSource mCurrentSource; + private boolean mCommunalEnabled; private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() { @Override @@ -46,7 +60,22 @@ public class CommunalSourceMonitor { @VisibleForTesting @Inject - public CommunalSourceMonitor() { + 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_ALL); + reloadSettings(); } /** @@ -62,13 +91,8 @@ public class CommunalSourceMonitor { mCurrentSource = source; - // If the new source is valid, inform registered Callbacks of its presence. - for (int i = 0; i < mCallbacks.size(); i++) { - Callback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onSourceAvailable( - mCurrentSource != null ? new WeakReference<>(mCurrentSource) : null); - } + if (mCommunalEnabled) { + executeOnSourceAvailableCallbacks(); } // Add callback to be informed when the source disconnects. @@ -77,6 +101,18 @@ public class CommunalSourceMonitor { } } + private void executeOnSourceAvailableCallbacks() { + // If the new source is valid, inform registered Callbacks of its presence. + for (WeakReference callback : mCallbacks) { + Callback cb = callback.get(); + if (cb != null) { + cb.onSourceAvailable( + (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>( + mCurrentSource) : null); + } + } + } + /** * Adds a {@link Callback} to receive {@link CommunalSource} updates. * @@ -86,7 +122,7 @@ public class CommunalSourceMonitor { mCallbacks.add(new WeakReference<>(callback)); // Inform the callback of any already present CommunalSource. - if (mCurrentSource != null) { + if (mCommunalEnabled && mCurrentSource != null) { callback.onSourceAvailable(new WeakReference<>(mCurrentSource)); } } @@ -100,6 +136,20 @@ public class CommunalSourceMonitor { mCallbacks.removeIf(el -> el.get() == callback); } + private void reloadSettings() { + boolean newCommunalEnabled = mSecureSettings.getInt(Settings.Secure.COMMUNAL_MODE_ENABLED, + 1) == 1; + + if (DEBUG) { + Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled); + } + + if (mCommunalEnabled != newCommunalEnabled) { + mCommunalEnabled = newCommunalEnabled; + executeOnSourceAvailableCallbacks(); + } + } + /** * Interface implemented to be notified when new {@link CommunalSource} become available. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java new file mode 100644 index 0000000000000..eb34cbb78a68f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java @@ -0,0 +1,151 @@ +/* + * 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 com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.os.Handler; +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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.MockitoAnnotations; + +import java.lang.ref.WeakReference; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class CommunalSourceMonitorTest extends SysuiTestCase { + 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); + } + + @Test + public void testSourceAddedBeforeCallbackAdded() { + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.setSource(source); + mCommunalSourceMonitor.addCallback(callback); + + verifyOnSourceAvailableCalledWith(callback, source); + } + + @Test + public void testRemoveCallback() { + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.addCallback(callback); + mCommunalSourceMonitor.removeCallback(callback); + mCommunalSourceMonitor.setSource(source); + + verify(callback, never()).onSourceAvailable(any()); + } + + @Test + public void testAddCallbackWithDefaultSetting() { + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.addCallback(callback); + mCommunalSourceMonitor.setSource(source); + + verifyOnSourceAvailableCalledWith(callback, source); + } + + @Test + public void testAddCallbackWithSettingDisabled() { + setCommunalEnabled(false); + + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.addCallback(callback); + mCommunalSourceMonitor.setSource(source); + + verify(callback, never()).onSourceAvailable(any()); + } + + @Test + public void testSettingEnabledAfterCallbackAdded() { + setCommunalEnabled(false); + + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.addCallback(callback); + mCommunalSourceMonitor.setSource(source); + + // 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); + verifyOnSourceAvailableCalledWith(callback, source); + } + + @Test + public void testSettingDisabledAfterCallbackAdded() { + final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class); + final CommunalSource source = mock(CommunalSource.class); + + mCommunalSourceMonitor.addCallback(callback); + mCommunalSourceMonitor.setSource(source); + verifyOnSourceAvailableCalledWith(callback, source); + + // The callback should execute again when the setting is disabled, with a value of null. + setCommunalEnabled(false); + verify(callback).onSourceAvailable(null); + } + + private void setCommunalEnabled(boolean enabled) { + mSecureSettings.putInt(Settings.Secure.COMMUNAL_MODE_ENABLED, enabled ? 1 : 0); + TestableLooper.get(this).processAllMessages(); + } + + private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback, + CommunalSource source) { + final ArgumentCaptor> sourceCapture = + ArgumentCaptor.forClass(WeakReference.class); + verify(callback).onSourceAvailable(sourceCapture.capture()); + assertThat(sourceCapture.getValue().get()).isEqualTo(source); + } +}