Migration of BridgingManager and BridgingConfig from WSL to AndroidX
Bug: 176894971
Test: Robolectric test
Relnote: "Move BridgingManager and BridgingConfig classes from Wear Support Library to Androidx"
Change-Id: I3a17e9cf1db86b731b96f0ee0cd45b238aa3fcea
diff --git a/wear/wear-phone-interactions/api/current.txt b/wear/wear-phone-interactions/api/current.txt
index f71e090..e73768b 100644
--- a/wear/wear-phone-interactions/api/current.txt
+++ b/wear/wear-phone-interactions/api/current.txt
@@ -16,3 +16,40 @@
}
+package androidx.wear.phone.interactions.notifications {
+
+ public final class BridgingConfig {
+ method public java.util.Set<java.lang.String>? getExcludedTags();
+ method public boolean isBridgingEnabled();
+ property public final java.util.Set<java.lang.String>? excludedTags;
+ property public final boolean isBridgingEnabled;
+ }
+
+ public static final class BridgingConfig.Builder {
+ ctor public BridgingConfig.Builder(android.content.Context context, boolean isBridgingEnabled);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTag(String tag);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTags(java.util.Collection<java.lang.String> tags);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig build();
+ }
+
+ public interface BridgingConfigurationHandler {
+ method public void applyBridgingConfiguration(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ }
+
+ public final class BridgingManager {
+ method public static androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ method public void setConfig(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ field public static final androidx.wear.phone.interactions.notifications.BridgingManager.Companion Companion;
+ }
+
+ public static final class BridgingManager.Companion {
+ method public androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ }
+
+ public final class BridgingManagerServiceBinder {
+ ctor public BridgingManagerServiceBinder(android.content.Context context, androidx.wear.phone.interactions.notifications.BridgingConfigurationHandler bridgingConfigurationHandler);
+ method public android.os.IBinder? getBinder();
+ }
+
+}
+
diff --git a/wear/wear-phone-interactions/api/public_plus_experimental_current.txt b/wear/wear-phone-interactions/api/public_plus_experimental_current.txt
index f71e090..e73768b 100644
--- a/wear/wear-phone-interactions/api/public_plus_experimental_current.txt
+++ b/wear/wear-phone-interactions/api/public_plus_experimental_current.txt
@@ -16,3 +16,40 @@
}
+package androidx.wear.phone.interactions.notifications {
+
+ public final class BridgingConfig {
+ method public java.util.Set<java.lang.String>? getExcludedTags();
+ method public boolean isBridgingEnabled();
+ property public final java.util.Set<java.lang.String>? excludedTags;
+ property public final boolean isBridgingEnabled;
+ }
+
+ public static final class BridgingConfig.Builder {
+ ctor public BridgingConfig.Builder(android.content.Context context, boolean isBridgingEnabled);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTag(String tag);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTags(java.util.Collection<java.lang.String> tags);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig build();
+ }
+
+ public interface BridgingConfigurationHandler {
+ method public void applyBridgingConfiguration(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ }
+
+ public final class BridgingManager {
+ method public static androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ method public void setConfig(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ field public static final androidx.wear.phone.interactions.notifications.BridgingManager.Companion Companion;
+ }
+
+ public static final class BridgingManager.Companion {
+ method public androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ }
+
+ public final class BridgingManagerServiceBinder {
+ ctor public BridgingManagerServiceBinder(android.content.Context context, androidx.wear.phone.interactions.notifications.BridgingConfigurationHandler bridgingConfigurationHandler);
+ method public android.os.IBinder? getBinder();
+ }
+
+}
+
diff --git a/wear/wear-phone-interactions/api/restricted_current.txt b/wear/wear-phone-interactions/api/restricted_current.txt
index f71e090..e73768b 100644
--- a/wear/wear-phone-interactions/api/restricted_current.txt
+++ b/wear/wear-phone-interactions/api/restricted_current.txt
@@ -16,3 +16,40 @@
}
+package androidx.wear.phone.interactions.notifications {
+
+ public final class BridgingConfig {
+ method public java.util.Set<java.lang.String>? getExcludedTags();
+ method public boolean isBridgingEnabled();
+ property public final java.util.Set<java.lang.String>? excludedTags;
+ property public final boolean isBridgingEnabled;
+ }
+
+ public static final class BridgingConfig.Builder {
+ ctor public BridgingConfig.Builder(android.content.Context context, boolean isBridgingEnabled);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTag(String tag);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTags(java.util.Collection<java.lang.String> tags);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig build();
+ }
+
+ public interface BridgingConfigurationHandler {
+ method public void applyBridgingConfiguration(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ }
+
+ public final class BridgingManager {
+ method public static androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ method public void setConfig(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ field public static final androidx.wear.phone.interactions.notifications.BridgingManager.Companion Companion;
+ }
+
+ public static final class BridgingManager.Companion {
+ method public androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ }
+
+ public final class BridgingManagerServiceBinder {
+ ctor public BridgingManagerServiceBinder(android.content.Context context, androidx.wear.phone.interactions.notifications.BridgingConfigurationHandler bridgingConfigurationHandler);
+ method public android.os.IBinder? getBinder();
+ }
+
+}
+
diff --git a/wear/wear-phone-interactions/build.gradle b/wear/wear-phone-interactions/build.gradle
index d78c282..b6e4126 100644
--- a/wear/wear-phone-interactions/build.gradle
+++ b/wear/wear-phone-interactions/build.gradle
@@ -28,6 +28,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
+ api("androidx.core:core:1.5.0-beta01")
api(KOTLIN_STDLIB)
testImplementation(ANDROIDX_TEST_CORE)
@@ -41,11 +42,15 @@
android {
defaultConfig {
- minSdkVersion 23
+ minSdkVersion 25
}
// Use Robolectric 4.+
testOptions.unitTests.includeAndroidResources = true
+
+ buildFeatures {
+ aidl = true
+ }
}
androidx {
diff --git a/wear/wear-phone-interactions/src/main/AndroidManifest.xml b/wear/wear-phone-interactions/src/main/AndroidManifest.xml
index 65f2d51..f111ff0 100644
--- a/wear/wear-phone-interactions/src/main/AndroidManifest.xml
+++ b/wear/wear-phone-interactions/src/main/AndroidManifest.xml
@@ -17,4 +17,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="androidx.wear.phone.interactions">
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/wear/wear-phone-interactions/src/main/aidl/android/support/wearable/notifications/IBridgingManagerService.aidl b/wear/wear-phone-interactions/src/main/aidl/android/support/wearable/notifications/IBridgingManagerService.aidl
new file mode 100644
index 0000000..d887837
--- /dev/null
+++ b/wear/wear-phone-interactions/src/main/aidl/android/support/wearable/notifications/IBridgingManagerService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 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 android.support.wearable.notifications;
+
+/**
+ * Interface of a service that allows setting the notification bridging configuration
+ *
+ * @hide
+ */
+interface IBridgingManagerService {
+ // IMPORTANT NOTE: All methods must be given an explicit transaction id that must never change
+ // in the future to remain binary backwards compatible.
+ // Next Id: 2
+
+ /**
+ * API version number. This should be incremented every time a new method is added.
+ */
+ const int API_VERSION = 1;
+
+ /**
+ * Sets the bridging configuration
+ *
+ * @since API version 1
+ */
+ void setBridgingConfig(in Bundle bridgingConfig) = 0;
+
+ /**
+ * Returns the version number for this API which the client can use to determine which methods
+ * are available.
+ *
+ * @since API version 1.
+ */
+ int getApiVersion() = 1;
+}
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingConfig.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingConfig.kt
new file mode 100644
index 0000000..de2eb7f
--- /dev/null
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingConfig.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 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 androidx.wear.phone.interactions.notifications
+
+import android.content.Context
+import android.os.Bundle
+import java.util.Objects
+import kotlin.collections.ArrayList
+import kotlin.collections.Collection
+import kotlin.collections.HashSet
+import kotlin.collections.MutableSet
+
+/**
+ * Bridging configuration to be specified at runtime, to set tags for notifications that are exempt
+ * from the bridging mode. specifically, create a [BridgingConfig] object, add excluded tags, then
+ * set it with [BridgingManager.setConfig]
+ *
+ * Specifying a bridging configuration at runtime overrides a bridging-related setting in the
+ * Android manifest file.
+ *
+ */
+public class BridgingConfig internal constructor(
+ /** Name of the package of the current context */
+ internal val packageName: String?,
+ /** Whether notification bridging is enabled in the configuration. */
+ public val isBridgingEnabled: Boolean,
+ /**
+ * The set of excluded tags in the configuration. The bridging mode for these tags is the
+ * opposite of the default mode (returned by [.isBridgingEnabled]).
+ */
+ public val excludedTags: MutableSet<String>?
+) {
+ internal companion object {
+ private const val TAG = "BridgingConfig"
+
+ private const val EXTRA_ORIGINAL_PACKAGE =
+ "android.support.wearable.notifications.extra.originalPackage"
+ private const val EXTRA_BRIDGING_ENABLED =
+ "android.support.wearable.notifications.extra.bridgingEnabled"
+ private const val EXTRA_EXCLUDED_TAGS =
+ "android.support.wearable.notifications.extra.excludedTags"
+
+ @JvmStatic
+ internal fun fromBundle(bundle: Bundle): BridgingConfig =
+ BridgingConfig(
+ bundle.getString(EXTRA_ORIGINAL_PACKAGE),
+ bundle.getBoolean(EXTRA_BRIDGING_ENABLED),
+ bundle.getStringArrayList(EXTRA_EXCLUDED_TAGS)?.toSet() as MutableSet<String>?
+ )
+ }
+
+ internal fun toBundle(context: Context): Bundle =
+ Bundle().apply {
+ putString(EXTRA_ORIGINAL_PACKAGE, context.getPackageName())
+ putBoolean(EXTRA_BRIDGING_ENABLED, isBridgingEnabled)
+ putStringArrayList(EXTRA_EXCLUDED_TAGS, excludedTags?.toList() as ArrayList<String>)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other is BridgingConfig) {
+ return other.isBridgingEnabled == isBridgingEnabled and
+ (other.excludedTags == excludedTags) and
+ (other.packageName == packageName)
+ }
+
+ return false
+ }
+
+ override fun hashCode(): Int = Objects.hash(packageName, isBridgingEnabled, excludedTags)
+
+ override fun toString(): String {
+ return "BridgingConfig{packageName='$packageName'" +
+ ", isBridgingEnabled='$isBridgingEnabled'" +
+ ", excludedTags=$excludedTags}"
+ }
+
+ /** Builder for BridgingConfig. */
+ public class Builder(context: Context, private val isBridgingEnabled: Boolean) {
+ private val packageName: String
+ private val excludedTags: MutableSet<String> = HashSet<String>()
+
+ // Initializes new instance of a Builder. By default the set of excluded tags is empty.
+ init {
+ packageName = context.packageName
+ }
+
+ /**
+ * Adds a tag for which the bridging mode is the opposite as the default mode.
+ *
+ * Examples:
+ *
+ * ```
+ * new BridgingConfig.Builder(context, false) // bridging disabled by default
+ * .addExcludedTag("foo")
+ * .addExcludedTag("bar")
+ * .build());
+ * ```
+ *
+ * ```
+ * new BridgingConfig.Builder(context, true) // bridging enabled by default
+ * .addExcludedTag("foo")
+ * .addExcludedTag("bar")
+ * .build());
+ * ```
+ *
+ * @param tag The tag to exclude from the default bridging mode.
+ * @return The Builder instance.
+ */
+ public fun addExcludedTag(tag: String): Builder {
+ excludedTags.add(tag)
+ return this
+ }
+
+ /**
+ * Sets a collection of tags for which the bridging mode is the opposite as the default mode.
+ *
+ * Examples:
+ *
+ * ```
+ * new BridgingConfig.Builder(context, false) // bridging disabled by default
+ * .addExcludedTags(Arrays.asList("foo", "bar", "baz"))
+ * .build());
+ *```
+ *
+ * ```
+ * new BridgingConfig.Builder(context, true) // bridging enabled by default
+ * .addExcludedTags(Arrays.asList("foo", "bar", "baz"))
+ * .build());
+ * }
+ * ```
+ *
+ * @param tags The collection of tags to exclude from the default bridging mode.
+ * @return The Builder instance.
+ */
+ @SuppressWarnings("MissingGetterMatchingBuilder")
+ // no getter needed for the builder, getter is provided in BridingConfig
+ public fun addExcludedTags(tags: Collection<String>): Builder {
+ excludedTags.addAll(tags)
+ return this
+ }
+
+ /** Builds a BridgingConfig object. */
+ public fun build(): BridgingConfig =
+ BridgingConfig(packageName, isBridgingEnabled, excludedTags)
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManager.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManager.kt
new file mode 100644
index 0000000..7f8b241
--- /dev/null
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManager.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright 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 androidx.wear.phone.interactions.notifications
+
+import android.annotation.SuppressLint
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.IBinder
+import android.os.RemoteException
+import android.support.wearable.notifications.IBridgingManagerService
+
+/**
+ * APIs to enable/disable notification bridging at runtime.
+ *
+ *
+ * Using a BridgingManager object, you can set a bridging mode, and optionally set tags for
+ * notifications that are exempt from the bridging mode. Specifically, create a [BridgingConfig]
+ * object and set is as shown in the example usages below:
+ *
+ *
+ * * Disable bridging at runtime:
+ * ```
+ * BridgingManager.fromContext(context).setConfig(
+ * new BridgingConfig.Builder(context, false).build()
+ * );
+ * ```
+ *
+ * * Disable bridging at runtime except for the tags "foo" and "bar":
+ * ```
+ * BridgingManager.fromContext(context).setConfig(
+ * new BridgingConfig.Builder(context, false)
+ * .addExcludedTag("foo")
+ * .addExcludedTag("bar")
+ * .build()
+ * );
+ * ```
+ *
+ * * Disable bridging at runtime except for the tags "foo" and "bar" and "baz":
+ * ```
+ * BridgingManager.fromContext(context).setConfig(
+ * new BridgingConfig.Builder(context, false)
+ * .addExcludedTags(Arrays.asList("foo", "bar", "baz"))
+ * .build()
+ * );
+ * ```
+ *
+ * * Adding a bridge tag to a notification posted on a phone:
+ * ```
+ * NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
+ * // ... set other fields ...
+ * .extend(
+ * new NotificationCompat.WearableExtender()
+ * .setBridgeTag("foo")
+ * );
+ * Notification notification = notificationBuilder.build();
+ * ```
+ *
+ * See also:
+ * * [BridgingConfig.Builder.addExcludedTag]
+ * * [BridgingConfig.Builder.addExcludedTags]
+ * * [androidx.core.app.NotificationCompat.WearableExtender.setBridgeTag]
+ */
+public class BridgingManager private constructor(private val context: Context) {
+ /**
+ * Sets the BridgingConfig object.
+ *
+ * @param bridgingConfig The BridgingConfig object.
+ *
+ * @throws RuntimeException if the service binding is failed.
+ */
+ @SuppressLint("SyntheticAccessor")
+ public fun setConfig(bridgingConfig: BridgingConfig) {
+ require(isWearableDevice(context)) { "API only supported on wearable devices" }
+ val connection = BridgingConfigServiceConnection(context, bridgingConfig)
+ val intent = Intent(ACTION_BIND_BRIDGING_MANAGER)
+ intent.setPackage(BRIDGING_CONFIG_SERVICE_PACKAGE)
+ if (!context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+ context.unbindService(connection)
+ throw RuntimeException("BridgingManager: Failed to bind service")
+ }
+ }
+
+ /** Class responsible for sending the bridge mode to ClockworkHome. */
+ private class BridgingConfigServiceConnection internal constructor(
+ private val context: Context,
+ bridgingConfig: BridgingConfig
+ ) : ServiceConnection {
+ private val bundle: Bundle = bridgingConfig.toBundle(context)
+
+ override fun onServiceConnected(className: ComponentName, binder: IBinder) {
+ val service: IBridgingManagerService = IBridgingManagerService.Stub.asInterface(binder)
+ try {
+ service.setBridgingConfig(bundle)
+ } catch (e: RemoteException) {
+ throw e.cause!!
+ }
+ context.unbindService(this)
+ }
+
+ override fun onServiceDisconnected(arg0: ComponentName) {
+ context.unbindService(this)
+ }
+ }
+
+ public companion object {
+ private const val ACTION_BIND_BRIDGING_MANAGER =
+ "android.support.wearable.notifications.action.BIND_BRIDGING_MANAGER"
+
+ private const val BRIDGING_CONFIG_SERVICE_PACKAGE = "com.google.android.wearable.app"
+
+ /**
+ * Create a BridgingManager instance with the passed in Context.
+ * @param context Context object.
+ */
+ @JvmStatic
+ public fun fromContext(context: Context): BridgingManager = BridgingManager(context)
+
+ private fun isWearableDevice(context: Context): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceBinder.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceBinder.kt
new file mode 100644
index 0000000..8961404
--- /dev/null
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceBinder.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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 androidx.wear.phone.interactions.notifications
+
+import android.app.Service
+import android.content.Context
+import android.os.Binder
+import android.os.Bundle
+import android.os.IBinder
+import android.support.wearable.notifications.IBridgingManagerService
+
+/**
+ * Handler for applying the notification bridging configuration when received by the service.
+ */
+public interface BridgingConfigurationHandler {
+ /**
+ * Apply the notification bridging configurations.
+ * @param bridgingConfig The received bridging configuration
+ */
+ public fun applyBridgingConfiguration(bridgingConfig: BridgingConfig)
+}
+
+/**
+ * Class for providing the binder that clients used to communicate with the service regarding
+ * notification bridging configurations.
+ */
+public class BridgingManagerServiceBinder(
+ private val context: Context,
+ private val bridgingConfigurationHandler: BridgingConfigurationHandler
+) {
+ /**
+ * Call this method in [Service.onBind] to provide the interface that clients
+ * use to communicate with the service by returning an IBinder.
+ */
+ public fun getBinder(): IBinder? =
+ BridgingManagerServiceImpl(context, bridgingConfigurationHandler)
+}
+
+internal class BridgingManagerServiceImpl(
+ private val context: Context,
+ private val bridgingConfigurationHandler: BridgingConfigurationHandler
+) : IBridgingManagerService.Stub() {
+
+ override fun getApiVersion(): Int = IBridgingManagerService.API_VERSION
+
+ override fun setBridgingConfig(bridgingConfigBundle: Bundle) {
+ val bridgingConfig = BridgingConfig.fromBundle(bridgingConfigBundle)
+ val packageName = bridgingConfig.packageName
+ val senderAppPackage: String? =
+ context.packageManager.getNameForUid(Binder.getCallingUid())
+ require(senderAppPackage == packageName) {
+ "Package invalid: $senderAppPackage not equals $packageName"
+ }
+ bridgingConfigurationHandler
+ .applyBridgingConfiguration(bridgingConfig)
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt
new file mode 100644
index 0000000..62ebed5
--- /dev/null
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 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 androidx.wear.phone.interactions.notifications
+
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.wear.phone.interactions.WearPhoneInteractionsTestRunner
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.internal.DoNotInstrument
+import java.util.Arrays
+import java.util.HashSet
+
+/** Unit tests for [BridgingManager]. */
+@RunWith(WearPhoneInteractionsTestRunner::class)
+@DoNotInstrument // Needed because it is defined in the "android" package.
+public class BridgingManagerTest {
+
+ private val mContext: Context = mock(Context::class.java)
+ private val packageManager: PackageManager = mock(PackageManager::class.java)
+
+ init {
+ `when`(mContext.packageName).thenReturn(PACKAGE_NAME)
+ `when`(mContext.packageManager).thenReturn(packageManager)
+ `when`(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true)
+ }
+
+ @Test
+ public fun disableBridging() {
+ val builder = BridgingConfig.Builder(
+ mContext, false
+ )
+
+ Assert.assertEquals(BridgingConfig(PACKAGE_NAME, false, HashSet()), builder.build())
+ }
+
+ @Test
+ public fun enableBridging() {
+ val builder = BridgingConfig.Builder(
+ mContext, true
+ )
+
+ Assert.assertEquals(BridgingConfig(PACKAGE_NAME, true, HashSet()), builder.build())
+ }
+
+ @Test
+ public fun bridgingEnableByDefault() {
+ val builder = BridgingConfig.Builder(
+ mContext, true
+ )
+
+ Assert.assertTrue(BridgingConfig(PACKAGE_NAME, true, HashSet()).equals(builder.build()))
+ }
+
+ @Test
+ public fun addTagsWithoutSettingBridging() {
+ val builder = BridgingConfig.Builder(
+ mContext, true
+ )
+ builder.addExcludedTag("foo")
+
+ Assert.assertEquals(
+ BridgingConfig(PACKAGE_NAME, true, HashSet(listOf("foo"))),
+ builder.build()
+ )
+ }
+
+ @Test
+ public fun disableBridgingWithTagsInSeparateCalls() {
+ val builder = BridgingConfig.Builder(
+ mContext, false
+ )
+ .addExcludedTag("foo")
+ .addExcludedTag("bar")
+ .addExcludedTag("foo")
+
+ Assert.assertEquals(
+ BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))),
+ builder.build()
+ )
+ }
+
+ @Test
+ public fun disableBridgingWithTagsInOneCall() {
+ val builder = BridgingConfig.Builder(
+ mContext, false
+ )
+ .addExcludedTags(Arrays.asList("foo", "bar", "foo"))
+
+ Assert.assertEquals(
+ BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))),
+ builder.build()
+ )
+ }
+
+ @Test
+ public fun disableBridgingWithTagsInMixOfCalls() {
+ val builder = BridgingConfig.Builder(
+ mContext, false
+ )
+ .addExcludedTag("123")
+ .addExcludedTags(Arrays.asList("foo", "bar", "foo"))
+ .addExcludedTags(Arrays.asList("foo", "bar", "abc"))
+ .addExcludedTag("aaa")
+ .addExcludedTag("foo")
+
+ Assert.assertEquals(
+ BridgingConfig(
+ PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar", "123", "aaa", "abc"))
+ ),
+ builder.build()
+ )
+ }
+
+ private companion object {
+ private const val PACKAGE_NAME = "foo_package"
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/test/resources/robolectric.properties b/wear/wear-phone-interactions/src/test/resources/robolectric.properties
index 185e1de..c0066c1 100644
--- a/wear/wear-phone-interactions/src/test/resources/robolectric.properties
+++ b/wear/wear-phone-interactions/src/test/resources/robolectric.properties
@@ -14,7 +14,7 @@
# limitations under the License.
#
-# Robolectric currently doesn't support API 30, so we have to explicitly specify 28 as the target
-# sdk for now.
-# TODO(b/177072877) Remove when no longer necessary.
-sdk=28
\ No newline at end of file
+# Robolectric currently doesn't support API 30, so we have to explicitly the target sdk levels for
+# now.
+# TODO(b/177072877): Remove when no longer necessary.
+sdk=25,26,27,28