mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-16 02:44:51 +00:00
feat(cast): clicking on the notif opens specified path
This commit is contained in:
@@ -16,5 +16,12 @@
|
|||||||
<action android:name="androidx.media3.session.MediaSessionService" />
|
<action android:name="androidx.media3.session.MediaSessionService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<activity
|
||||||
|
android:name="dev.zoriya.omni.OmniCastTapActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:noHistory="true"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -26,11 +26,7 @@ class OmniCastOptionsProvider : OptionsProvider {
|
|||||||
.setSmallIconDrawableResId(
|
.setSmallIconDrawableResId(
|
||||||
androidx.media3.session.R.drawable.media3_notification_small_icon
|
androidx.media3.session.R.drawable.media3_notification_small_icon
|
||||||
)
|
)
|
||||||
.apply {
|
.setTargetActivityClassName(OmniCastTapActivity::class.java.name)
|
||||||
context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
||||||
?.component?.className
|
|
||||||
?.let { setTargetActivityClassName(it) }
|
|
||||||
}
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val mediaOptions = CastMediaOptions.Builder()
|
val mediaOptions = CastMediaOptions.Builder()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,6 +128,12 @@ class OmniPlayer(
|
|||||||
|
|
||||||
val player: Player = runOnMainThreadSync {
|
val player: Player = runOnMainThreadSync {
|
||||||
castOptions?.receiverApplicationId?.let { receiverApplicationId = it }
|
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 {
|
val cc = try {
|
||||||
CastContext.getSharedInstance(ctx)
|
CastContext.getSharedInstance(ctx)
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
@@ -648,6 +654,10 @@ class OmniPlayer(
|
|||||||
// Receiver application id passed at runtime via OmniProvider's `cast`
|
// Receiver application id passed at runtime via OmniProvider's `cast`
|
||||||
// prop; read by OmniCastOptionsProvider when the Cast SDK initializes.
|
// prop; read by OmniCastOptionsProvider when the Cast SDK initializes.
|
||||||
var receiverApplicationId: String? = null
|
var receiverApplicationId: String? = null
|
||||||
|
|
||||||
|
// `cast.notificationUrl`, read back by OmniCastTapActivity.
|
||||||
|
const val OMNI_PREFS = "dev.zoriya.omni"
|
||||||
|
const val NOTIFICATION_URL_PREF = "notificationUrl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -511,7 +511,10 @@ function App(): React.JSX.Element {
|
|||||||
<OmniProvider
|
<OmniProvider
|
||||||
source={hasSource ? source : undefined}
|
source={hasSource ? source : undefined}
|
||||||
backend={{ android: backend }}
|
backend={{ android: backend }}
|
||||||
cast={{ receiverApplicationId: "D8FB0FC1" }}
|
cast={{
|
||||||
|
receiverApplicationId: "D8FB0FC1",
|
||||||
|
notificationUrl: "omniexample://remote",
|
||||||
|
}}
|
||||||
showNotification
|
showNotification
|
||||||
>
|
>
|
||||||
<PlayerExample
|
<PlayerExample
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="omniexample" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
|
|||||||
+7
-3
@@ -34,8 +34,11 @@ namespace margelo::nitro::omni {
|
|||||||
static const auto clazz = javaClassStatic();
|
static const auto clazz = javaClassStatic();
|
||||||
static const auto fieldReceiverApplicationId = clazz->getField<jni::JString>("receiverApplicationId");
|
static const auto fieldReceiverApplicationId = clazz->getField<jni::JString>("receiverApplicationId");
|
||||||
jni::local_ref<jni::JString> receiverApplicationId = this->getFieldValue(fieldReceiverApplicationId);
|
jni::local_ref<jni::JString> receiverApplicationId = this->getFieldValue(fieldReceiverApplicationId);
|
||||||
|
static const auto fieldNotificationUrl = clazz->getField<jni::JString>("notificationUrl");
|
||||||
|
jni::local_ref<jni::JString> notificationUrl = this->getFieldValue(fieldNotificationUrl);
|
||||||
return CastOptions(
|
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]]
|
[[maybe_unused]]
|
||||||
static jni::local_ref<JCastOptions::javaobject> fromCpp(const CastOptions& value) {
|
static jni::local_ref<JCastOptions::javaobject> fromCpp(const CastOptions& value) {
|
||||||
using JSignature = JCastOptions(jni::alias_ref<jni::JString>);
|
using JSignature = JCastOptions(jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>);
|
||||||
static const auto clazz = javaClassStatic();
|
static const auto clazz = javaClassStatic();
|
||||||
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
||||||
return create(
|
return create(
|
||||||
clazz,
|
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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ import java.util.Objects
|
|||||||
data class CastOptions(
|
data class CastOptions(
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
@Keep
|
@Keep
|
||||||
val receiverApplicationId: String?
|
val receiverApplicationId: String?,
|
||||||
|
@DoNotStrip
|
||||||
|
@Keep
|
||||||
|
val notificationUrl: String?
|
||||||
) {
|
) {
|
||||||
/* primary constructor */
|
/* primary constructor */
|
||||||
|
|
||||||
@@ -28,11 +31,13 @@ data class CastOptions(
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is CastOptions) return false
|
if (other !is CastOptions) return false
|
||||||
return Objects.deepEquals(this.receiverApplicationId, other.receiverApplicationId)
|
return Objects.deepEquals(this.receiverApplicationId, other.receiverApplicationId)
|
||||||
|
&& Objects.deepEquals(this.notificationUrl, other.notificationUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return arrayOf<Any?>(
|
return arrayOf<Any?>(
|
||||||
receiverApplicationId
|
receiverApplicationId,
|
||||||
|
notificationUrl
|
||||||
).contentDeepHashCode()
|
).contentDeepHashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +49,8 @@ data class CastOptions(
|
|||||||
@Keep
|
@Keep
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
private fun fromCpp(receiverApplicationId: String?): CastOptions {
|
private fun fromCpp(receiverApplicationId: String?, notificationUrl: String?): CastOptions {
|
||||||
return CastOptions(receiverApplicationId)
|
return CastOptions(receiverApplicationId, notificationUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -41,10 +41,11 @@ namespace margelo::nitro::omni {
|
|||||||
struct CastOptions final {
|
struct CastOptions final {
|
||||||
public:
|
public:
|
||||||
std::optional<std::string> receiverApplicationId SWIFT_PRIVATE;
|
std::optional<std::string> receiverApplicationId SWIFT_PRIVATE;
|
||||||
|
std::optional<std::string> notificationUrl SWIFT_PRIVATE;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CastOptions() = default;
|
CastOptions() = default;
|
||||||
explicit CastOptions(std::optional<std::string> receiverApplicationId): receiverApplicationId(receiverApplicationId) {}
|
explicit CastOptions(std::optional<std::string> receiverApplicationId, std::optional<std::string> notificationUrl): receiverApplicationId(receiverApplicationId), notificationUrl(notificationUrl) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
friend bool operator==(const CastOptions& lhs, const CastOptions& rhs) = default;
|
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) {
|
static inline margelo::nitro::omni::CastOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
||||||
jsi::Object obj = arg.asObject(runtime);
|
jsi::Object obj = arg.asObject(runtime);
|
||||||
return margelo::nitro::omni::CastOptions(
|
return margelo::nitro::omni::CastOptions(
|
||||||
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId")))
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId"))),
|
||||||
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::omni::CastOptions& arg) {
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::omni::CastOptions& arg) {
|
||||||
jsi::Object obj(runtime);
|
jsi::Object obj(runtime);
|
||||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.receiverApplicationId));
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.receiverApplicationId));
|
||||||
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.notificationUrl));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
||||||
@@ -77,6 +80,7 @@ namespace margelo::nitro {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId")))) return false;
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "receiverApplicationId")))) return false;
|
||||||
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "notificationUrl")))) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ export interface Source {
|
|||||||
|
|
||||||
export interface CastOptions {
|
export interface CastOptions {
|
||||||
receiverApplicationId?: string;
|
receiverApplicationId?: string;
|
||||||
|
// deep link opened when the cast notification is tapped (android only)
|
||||||
|
notificationUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoSrc {
|
export interface VideoSrc {
|
||||||
|
|||||||
Reference in New Issue
Block a user