Merge changes from topic "primer-refactor"

* changes:
  Load CommunalSource.Connector from config.
  CommunalSourcePackageObserver Introduction.
This commit is contained in:
Bryce Lee
2021-12-03 23:44:51 +00:00
committed by Android (Google) Code Review
4 changed files with 242 additions and 0 deletions

View File

@@ -742,4 +742,8 @@
<!-- Flag to enable dream overlay service and its registration -->
<bool name="config_dreamOverlayServiceEnabled">false</bool>
<!-- Class for the communal source connector to be used -->
<string name="config_communalSourceConnector" translatable="false"></string>
</resources>

View File

@@ -0,0 +1,101 @@
/*
* 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 android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PatternMatcher;
import android.util.Log;
import com.google.android.collect.Lists;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
/**
* {@link PackageObserver} allows for monitoring the system for changes relating to a particular
* package. This can be used by {@link CommunalSource} clients to detect when a related package
* has changed and reloading is necessary.
*/
public class PackageObserver implements CommunalSource.Observer {
private static final String TAG = "PackageObserver";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.d(TAG, "package added receiver - onReceive");
}
final Iterator<WeakReference<Callback>> iter = mCallbacks.iterator();
while (iter.hasNext()) {
final Callback callback = iter.next().get();
if (callback != null) {
callback.onSourceChanged();
} else {
iter.remove();
}
}
}
};
private final String mPackageName;
private final Context mContext;
public PackageObserver(Context context, String packageName) {
mContext = context;
mPackageName = packageName;
}
@Override
public void addCallback(Callback callback) {
if (DEBUG) {
Log.d(TAG, "addCallback:" + callback);
}
mCallbacks.add(new WeakReference<>(callback));
// Only register for listening to package additions on first callback.
if (mCallbacks.size() > 1) {
return;
}
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
filter.addDataSchemeSpecificPart(mPackageName, PatternMatcher.PATTERN_LITERAL);
// Note that we directly register the receiver here as data schemes are not supported by
// BroadcastDispatcher.
mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
}
@Override
public void removeCallback(Callback callback) {
if (DEBUG) {
Log.d(TAG, "removeCallback:" + callback);
}
final boolean removed = mCallbacks.removeIf(el -> el.get() == callback);
if (removed && mCallbacks.isEmpty()) {
mContext.unregisterReceiver(mReceiver);
}
}
}

View File

@@ -16,26 +16,40 @@
package com.android.systemui.communal.dagger;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.communal.CommunalSource;
import com.android.systemui.communal.PackageObserver;
import com.android.systemui.communal.conditions.CommunalCondition;
import com.android.systemui.communal.conditions.CommunalSettingCondition;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Provider;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ElementsIntoSet;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
/**
* Dagger Module providing Communal-related functionality.
@@ -56,6 +70,20 @@ public interface CommunalModule {
return view;
}
/** */
@Provides
static Optional<CommunalSource.Observer> provideCommunalSourcePackageObserver(
Context context, @Main Resources resources) {
final String componentName = resources.getString(R.string.config_communalSourceComponent);
if (TextUtils.isEmpty(componentName)) {
return Optional.empty();
}
return Optional.of(new PackageObserver(context,
ComponentName.unflattenFromString(componentName).getPackageName()));
}
/**
* Provides LightSensorEventsDebounceAlgorithm as an instance to DebounceAlgorithm interface.
* @param algorithm the instance of algorithm that is bound to the interface.
@@ -75,4 +103,36 @@ public interface CommunalModule {
CommunalSettingCondition communalSettingCondition) {
return new HashSet<>(Collections.singletonList(communalSettingCondition));
}
/**
* TODO(b/205638389): Remove when there is a base implementation of
* {@link CommunalSource.Connector}. Currently a place holder to allow a map to be present.
*/
@Provides
@IntoMap
@Nullable
@StringKey("empty")
static CommunalSource.Connector provideEmptyCommunalSourceConnector() {
return null;
}
/** */
@Provides
static Optional<CommunalSource.Connector> provideCommunalSourceConnector(
@Main Resources resources,
Map<Class<?>, Provider<CommunalSource.Connector>> connectorCreators) {
final String className = resources.getString(R.string.config_communalSourceConnector);
if (TextUtils.isEmpty(className)) {
return Optional.empty();
}
try {
Class<?> clazz = Class.forName(className);
Provider<CommunalSource.Connector> provider = connectorCreators.get(clazz);
return provider != null ? Optional.of(provider.get()) : Optional.empty();
} catch (ClassNotFoundException e) {
return Optional.empty();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PackageObserverTest extends SysuiTestCase {
private static final String PACKAGE_NAME = "com.foo.bar";
@Mock
Context mContext;
@Mock
CommunalSource.Observer.Callback mCallback;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testChange() {
final PackageObserver observer = new PackageObserver(mContext, PACKAGE_NAME);
final ArgumentCaptor<BroadcastReceiver> receiverCapture =
ArgumentCaptor.forClass(BroadcastReceiver.class);
observer.addCallback(mCallback);
// Verify broadcast receiver registered.
verify(mContext).registerReceiver(receiverCapture.capture(), any(), anyInt());
// Simulate package change.
receiverCapture.getValue().onReceive(mContext, new Intent());
// Check that callback was informed.
verify(mCallback).onSourceChanged();
observer.removeCallback(mCallback);
// Make sure receiver is unregistered on last callback removal
verify(mContext).unregisterReceiver(receiverCapture.getValue());
}
}