diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index de57c05..7db277c 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -16,5 +16,12 @@
+
diff --git a/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt b/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt
index e2d1d7e..2e5a2b9 100644
--- a/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt
+++ b/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt
@@ -26,11 +26,7 @@ class OmniCastOptionsProvider : OptionsProvider {
.setSmallIconDrawableResId(
androidx.media3.session.R.drawable.media3_notification_small_icon
)
- .apply {
- context.packageManager.getLaunchIntentForPackage(context.packageName)
- ?.component?.className
- ?.let { setTargetActivityClassName(it) }
- }
+ .setTargetActivityClassName(OmniCastTapActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
diff --git a/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt b/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt
new file mode 100644
index 0000000..2fc3719
--- /dev/null
+++ b/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt
@@ -0,0 +1,30 @@
+package dev.zoriya.omni
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.core.net.toUri
+
+class OmniCastTapActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val url = getSharedPreferences(OmniPlayer.OMNI_PREFS, Context.MODE_PRIVATE)
+ .getString(OmniPlayer.NOTIFICATION_URL_PREF, null)
+ val intent = url
+ ?.let { Intent(Intent.ACTION_VIEW, it.toUri()).setPackage(packageName) }
+ ?: packageManager.getLaunchIntentForPackage(packageName)
+
+ try {
+ startActivity(
+ intent?.apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
+ )
+ } catch (e: Throwable) {
+ android.util.Log.w("OmniPlayer", "could not open $url", e)
+ }
+ finish()
+ }
+}
diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
index 339c013..b9eea17 100644
--- a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
+++ b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
@@ -128,6 +128,12 @@ class OmniPlayer(
val player: Player = runOnMainThreadSync {
castOptions?.receiverApplicationId?.let { receiverApplicationId = it }
+ // the cast notification outlives the app, so OmniCastTapActivity has to find the
+ // url back even when nothing of ours ran in that process yet.
+ ctx.getSharedPreferences(OMNI_PREFS, Context.MODE_PRIVATE)
+ .edit()
+ .putString(NOTIFICATION_URL_PREF, castOptions?.notificationUrl)
+ .apply()
val cc = try {
CastContext.getSharedInstance(ctx)
} catch (_: Throwable) {
@@ -648,6 +654,10 @@ class OmniPlayer(
// Receiver application id passed at runtime via OmniProvider's `cast`
// prop; read by OmniCastOptionsProvider when the Cast SDK initializes.
var receiverApplicationId: String? = null
+
+ // `cast.notificationUrl`, read back by OmniCastTapActivity.
+ const val OMNI_PREFS = "dev.zoriya.omni"
+ const val NOTIFICATION_URL_PREF = "notificationUrl"
}
}
diff --git a/example/App.tsx b/example/App.tsx
index 1dca28b..1d032ea 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -511,7 +511,10 @@ function App(): React.JSX.Element {
+
+
+
+
+
+
getField("receiverApplicationId");
jni::local_ref receiverApplicationId = this->getFieldValue(fieldReceiverApplicationId);
+ static const auto fieldNotificationUrl = clazz->getField("notificationUrl");
+ jni::local_ref notificationUrl = this->getFieldValue(fieldNotificationUrl);
return CastOptions(
- receiverApplicationId != nullptr ? std::make_optional(receiverApplicationId->toStdString()) : std::nullopt
+ receiverApplicationId != nullptr ? std::make_optional(receiverApplicationId->toStdString()) : std::nullopt,
+ notificationUrl != nullptr ? std::make_optional(notificationUrl->toStdString()) : std::nullopt
);
}
@@ -45,12 +48,13 @@ namespace margelo::nitro::omni {
*/
[[maybe_unused]]
static jni::local_ref fromCpp(const CastOptions& value) {
- using JSignature = JCastOptions(jni::alias_ref);
+ using JSignature = JCastOptions(jni::alias_ref, jni::alias_ref);
static const auto clazz = javaClassStatic();
static const auto create = clazz->getStaticMethod("fromCpp");
return create(
clazz,
- value.receiverApplicationId.has_value() ? jni::make_jstring(value.receiverApplicationId.value()) : nullptr
+ value.receiverApplicationId.has_value() ? jni::make_jstring(value.receiverApplicationId.value()) : nullptr,
+ value.notificationUrl.has_value() ? jni::make_jstring(value.notificationUrl.value()) : nullptr
);
}
};
diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/CastOptions.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/CastOptions.kt
index a8b165b..4cb72d4 100644
--- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/CastOptions.kt
+++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/CastOptions.kt
@@ -20,7 +20,10 @@ import java.util.Objects
data class CastOptions(
@DoNotStrip
@Keep
- val receiverApplicationId: String?
+ val receiverApplicationId: String?,
+ @DoNotStrip
+ @Keep
+ val notificationUrl: String?
) {
/* primary constructor */
@@ -28,11 +31,13 @@ data class CastOptions(
if (this === other) return true
if (other !is CastOptions) return false
return Objects.deepEquals(this.receiverApplicationId, other.receiverApplicationId)
+ && Objects.deepEquals(this.notificationUrl, other.notificationUrl)
}
override fun hashCode(): Int {
return arrayOf(
- receiverApplicationId
+ receiverApplicationId,
+ notificationUrl
).contentDeepHashCode()
}
@@ -44,8 +49,8 @@ data class CastOptions(
@Keep
@Suppress("unused")
@JvmStatic
- private fun fromCpp(receiverApplicationId: String?): CastOptions {
- return CastOptions(receiverApplicationId)
+ private fun fromCpp(receiverApplicationId: String?, notificationUrl: String?): CastOptions {
+ return CastOptions(receiverApplicationId, notificationUrl)
}
}
}
diff --git a/nitrogen/generated/shared/c++/CastOptions.hpp b/nitrogen/generated/shared/c++/CastOptions.hpp
index ce87273..5eba117 100644
--- a/nitrogen/generated/shared/c++/CastOptions.hpp
+++ b/nitrogen/generated/shared/c++/CastOptions.hpp
@@ -41,10 +41,11 @@ namespace margelo::nitro::omni {
struct CastOptions final {
public:
std::optional receiverApplicationId SWIFT_PRIVATE;
+ std::optional notificationUrl SWIFT_PRIVATE;
public:
CastOptions() = default;
- explicit CastOptions(std::optional receiverApplicationId): receiverApplicationId(receiverApplicationId) {}
+ explicit CastOptions(std::optional receiverApplicationId, std::optional notificationUrl): receiverApplicationId(receiverApplicationId), notificationUrl(notificationUrl) {}
public:
friend bool operator==(const CastOptions& lhs, const CastOptions& rhs) = default;
@@ -60,12 +61,14 @@ namespace margelo::nitro {
static inline margelo::nitro::omni::CastOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
jsi::Object obj = arg.asObject(runtime);
return margelo::nitro::omni::CastOptions(
- JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId")))
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId"))),
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl")))
);
}
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::omni::CastOptions& arg) {
jsi::Object obj(runtime);
obj.setProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId"), JSIConverter>::toJSI(runtime, arg.receiverApplicationId));
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl"), JSIConverter>::toJSI(runtime, arg.notificationUrl));
return obj;
}
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -77,6 +80,7 @@ namespace margelo::nitro {
return false;
}
if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId")))) return false;
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl")))) return false;
return true;
}
};
diff --git a/src/types/source.ts b/src/types/source.ts
index 342072e..4716a17 100644
--- a/src/types/source.ts
+++ b/src/types/source.ts
@@ -16,6 +16,8 @@ export interface Source {
export interface CastOptions {
receiverApplicationId?: string;
+ // deep link opened when the cast notification is tapped (android only)
+ notificationUrl?: string;
}
export interface VideoSrc {