Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursor/skills/agent-skills
Submodule agent-skills added at a4f602
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
RNBootSplash.init(this, R.style.BootTheme)
super.onCreate(null)

// Handle notification intents
intent?.let { NotificationIntentHandler.handleIntent(this, it) }
}

public override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)

// Handle notification intents when activity is already running
NotificationIntentHandler.handleIntent(this, intent)
}

override fun invokeDefaultOnBackPressed() {
moveTaskToBack(true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import android.os.Bundle
import android.util.Log
import com.google.gson.GsonBuilder
import chat.rocket.reactnative.voip.VoipNotification
import chat.rocket.reactnative.voip.VoipModule
import chat.rocket.reactnative.voip.VoipPayload
import android.os.Build
import android.app.KeyguardManager
import android.app.Activity

/**
* Handles notification Intent processing from MainActivity.
Expand All @@ -27,8 +22,7 @@ class NotificationIntentHandler {
*/
@JvmStatic
fun handleIntent(context: Context, intent: Intent) {
// Handle VoIP action first
if (handleVoipIntent(context, intent)) {
if (VoipNotification.handleMainActivityVoipIntent(context, intent)) {
return
}

Expand All @@ -41,41 +35,6 @@ class NotificationIntentHandler {
handleNotificationIntent(context, intent)
}

/**
* Handles VoIP call notification Intent.
* @return true if this was a VoIP intent, false otherwise
*/
@JvmStatic
private fun handleVoipIntent(context: Context, intent: Intent): Boolean {
if (!intent.getBooleanExtra("voipAction", false)) {
return false
}
val voipPayload = VoipPayload.fromBundle(intent.extras)
if (voipPayload == null || !voipPayload.isVoipIncomingCall()) {
return false
}

Log.d(TAG, "Handling VoIP intent - voipPayload: $voipPayload")

VoipNotification.cancelById(context, voipPayload.notificationId)
VoipNotification.cancelTimeout(voipPayload.callId)
VoipModule.storeInitialEvents(voipPayload)

if (context is Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
context.setShowWhenLocked(true)
context.setTurnScreenOn(true)
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(context, null)
}
}

// Clear the voip flag to prevent re-processing
intent.removeExtra("voipAction")

return true
}

/**
* Handles video conference notification Intent.
* @return true if this was a video conf intent, false otherwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import android.widget.FrameLayout
import android.util.Log
import android.view.ViewOutlineProvider
import com.bumptech.glide.Glide
import chat.rocket.reactnative.MainActivity
import chat.rocket.reactnative.R
import android.graphics.Typeface
import chat.rocket.reactnative.notification.Ejson
Expand Down Expand Up @@ -283,15 +282,8 @@ class IncomingCallActivity : Activity() {
clearTimeout()
VoipNotification.cancelTimeout(payload.callId)
stopRingtone()

// Launch MainActivity with call data
val launchIntent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtras(payload.toBundle())
}
startActivity(launchIntent)

finish()
VoipNotification.handleAcceptAction(this, payload)
// Activity finishes when ACTION_DISMISS is broadcast from handleAcceptAction (async DDP).
}

private fun handleDecline(payload: VoipPayload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class VoipModule(reactContext: ReactApplicationContext) : NativeVoipSpec(reactCo
companion object {
private const val TAG = "RocketChat.VoipModule"
private const val EVENT_INITIAL_EVENTS = "VoipPushInitialEvents"
private const val EVENT_VOIP_ACCEPT_FAILED = "VoipAcceptFailed"

private var reactContextRef: WeakReference<ReactApplicationContext>? = null
private var initialEventsData: VoipPayload? = null
Expand Down Expand Up @@ -57,6 +58,30 @@ class VoipModule(reactContext: ReactApplicationContext) : NativeVoipSpec(reactCo
emitInitialEventsEvent(voipPayload)
}

/**
* Stash native accept failure for cold start [getInitialEvents] and emit [EVENT_VOIP_ACCEPT_FAILED] when JS is running.
*/
@JvmStatic
fun storeAcceptFailureForJs(payload: VoipPayload) {
val failed = payload.copy(voipAcceptFailed = true)
initialEventsData = failed
emitVoipAcceptFailedEvent(failed)
}

private fun emitVoipAcceptFailedEvent(voipPayload: VoipPayload) {
try {
reactContextRef?.get()?.let { context ->
if (context.hasActiveReactInstance()) {
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(EVENT_VOIP_ACCEPT_FAILED, voipPayload.toWritableMap())
}
}
} catch (e: Exception) {
Log.e(TAG, "Failed to emit VoipAcceptFailed", e)
}
}

@JvmStatic
fun clearInitialEventsInternal() {
try {
Expand Down
Loading