Skip to content

Commit

Permalink
Dual-Platform unified Lifecycle Observer
Browse files Browse the repository at this point in the history
  • Loading branch information
yuroyami committed Apr 3, 2024
1 parent 7c843b1 commit c42dea6
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.yuroyami.syncplay.settings.valueBlockingly
import com.yuroyami.syncplay.settings.valueSuspendingly
import com.yuroyami.syncplay.utils.UIUtils.cutoutMode
import com.yuroyami.syncplay.utils.UIUtils.hideSystemUI
import com.yuroyami.syncplay.utils.bindWatchdog
import com.yuroyami.syncplay.utils.changeLanguage
import com.yuroyami.syncplay.utils.defaultEngineAndroid
import com.yuroyami.syncplay.utils.loggy
Expand Down Expand Up @@ -166,6 +167,9 @@ class WatchActivity : ComponentActivity() {

}

/** Communicates the lifecycle with our common code */
bindWatchdog()

/** Setting content view, making everything visible */
setContent {
RoomUI()
Expand Down
42 changes: 20 additions & 22 deletions iosApp/iosApp/SpProtocolIApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import NIO
import NIOFoundationCompat
import shared

class SpProtocolApple: SyncplayProtocol {
class SpProtocolApple: SyncplayProtocol, ChannelInboundHandler {
typealias InboundIn = ByteBuffer

private var channel: Channel?
private var eventLoopGroup: EventLoopGroup?


override func connectSocket() {
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
eventLoopGroup = group
Expand All @@ -17,14 +18,14 @@ class SpProtocolApple: SyncplayProtocol {
let bootstrap = ClientBootstrap(group: group)
.connectTimeout(TimeAmount.seconds(10))
.channelInitializer { channel in
channel.pipeline.addHandler(Reader())
channel.pipeline.addHandler(self)
}

let host = session.serverHost
let port = Int(session.serverPort)

do {
channel = try bootstrap.connect(host: host, port: port).wait()
channel = try? bootstrap.connect(host: host, port: port).wait()
} catch {
print(error)
syncplayCallback?.onConnectionFailed()
Expand All @@ -37,7 +38,6 @@ class SpProtocolApple: SyncplayProtocol {
}
}


override func isSocketValid() -> Bool {
return channel?.isActive ?? false
}
Expand Down Expand Up @@ -78,26 +78,24 @@ class SpProtocolApple: SyncplayProtocol {
// TLS setup for SwiftNIO
}

class Reader: ChannelInboundHandler {
typealias InboundIn = ByteBuffer

/** Channel handler stuff */
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
var buffer = unwrapInboundIn(data)
let readableBytes = buffer.readableBytes

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
var buffer = unwrapInboundIn(data)
let readableBytes = buffer.readableBytes

if let received = buffer.readString(length: readableBytes) {
handleJSON(json: received)
}
if let received = buffer.readString(length: readableBytes) {
handleJSON(json: received)
}
}

func channelReadComplete(context: ChannelHandlerContext) {
context.flush()
}
func channelReadComplete(context: ChannelHandlerContext) {
//context.flush()
}

func errorCaught(context: ChannelHandlerContext, error: Error) {
print("Reader exception: \(error)")
onError()
//context.close(promise: nil)
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("Reader exception: \(error)")
//onError()
//context.close(promise: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import android.database.Cursor
import android.database.sqlite.SQLiteException
import android.net.Uri
import android.provider.MediaStore
import androidx.activity.ComponentActivity
import androidx.annotation.WorkerThread
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import com.yuroyami.syncplay.models.MediaFile
import com.yuroyami.syncplay.watchroom.viewmodel
import java.io.File
import java.util.Locale

Expand Down Expand Up @@ -71,4 +75,20 @@ fun getPathFromURI(context: Context, contentUri: Uri): String {
} finally {
if (cursor != null && !cursor.isClosed) cursor.close()
}
}
}

fun ComponentActivity.bindWatchdog() {
val watchdog = viewmodel!!.lifecycleWatchdog
val lifecycleObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> watchdog.onCreate()
Lifecycle.Event.ON_START -> watchdog.onStart()
Lifecycle.Event.ON_RESUME -> watchdog.onResume()
Lifecycle.Event.ON_PAUSE -> watchdog.onPause()
Lifecycle.Event.ON_STOP -> watchdog.onStop()
else -> {}
}
}
lifecycle.addObserver(lifecycleObserver)
}

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ abstract class SyncplayProtocol {
var tls: Constants.TLS = Constants.TLS.TLS_NO

/** Coroutine scopes and dispatchers */
val protoScope = CoroutineScope(Dispatchers.IO)
val protoScope = CoroutineScope(Dispatchers.Default)

/** This method is responsible for bootstrapping (initializing) the Ktor TCP socket */
open fun connect() {
Expand All @@ -61,6 +61,8 @@ abstract class SyncplayProtocol {

loggy("PROTOCOL: Connected! HOST: ${session.serverHost}, PORT: ${session.serverPort}....", 202)

delay(500)

/** if the TLS mode is [Constants.TLS.TLS_ASK], then the the first packet to send
* concerns an opportunistic TLS check with the server, otherwise, a Hello would be first */
if (tls == Constants.TLS.TLS_ASK) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,20 +361,6 @@ private val settingsGLOBAL: List<Pair<Setting<out Any>, String>>
) to CATEG_GLOBAL_EXOPLAYER
)

if (getPlatform() == PLATFORM.Android) {
add(
BooleanSetting(
type = SettingType.ToggleSettingType,
key = PREF_TLS_ENABLE,
title = lyricist.strings.settingTlsTitle,
summary = lyricist.strings.settingTlsSummary,
defaultValue = getPlatform() == PLATFORM.Android, //StartTLS is not implemented in iOS
icon = Icons.Filled.Key,
styling = settingGLOBALstyle,
) to CATEG_GLOBAL_NETWORK
)
}

add(
MultiChoiceSetting(
type = SettingType.MultiChoicePopupSettingType,
Expand Down Expand Up @@ -402,6 +388,17 @@ private val settingsGLOBAL: List<Pair<Setting<out Any>, String>>
) to CATEG_GLOBAL_NETWORK
)

add(
BooleanSetting(
type = SettingType.ToggleSettingType,
key = PREF_TLS_ENABLE,
title = lyricist.strings.settingTlsTitle,
summary = lyricist.strings.settingTlsSummary,
defaultValue = getPlatform() == PLATFORM.Android, //StartTLS is not implemented in iOS
icon = Icons.Filled.Key,
styling = settingGLOBALstyle,
) to CATEG_GLOBAL_NETWORK
)

add(
Setting.YesNoDialogSetting(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.yuroyami.syncplay.ui

interface LifecycleWatchdog {

/**
* Called when the component is visible and interactive.
* @author Equivalent to viewDidAppear on iOS.
*/
fun onResume()

/**
* Called when the component is no longer visible.
* @author Equivalent to viewDidDisappear on iOS.
*/
fun onStop()

/**
* Called when the component is first created.
* @author Equivalent to viewDidLoad on iOS.
*/
fun onCreate()

/**
* Called when the component is becoming visible.
* @author Equivalent to viewWillAppear on iOS.
*/
fun onStart()

/**
* Called when the component is no longer visible but still partially visible.
* @author Equivalent to viewWillDisappear on iOS.
*/
fun onPause()

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.yuroyami.syncplay.settings.DataStoreKeys
import com.yuroyami.syncplay.settings.DataStoreKeys.PREF_PAUSE_ON_SOMEONE_LEAVE
import com.yuroyami.syncplay.settings.DataStoreKeys.PREF_TLS_ENABLE
import com.yuroyami.syncplay.settings.valueBlockingly
import com.yuroyami.syncplay.ui.LifecycleWatchdog
import com.yuroyami.syncplay.utils.PLATFORM
import com.yuroyami.syncplay.utils.PlaylistUtils
import com.yuroyami.syncplay.utils.RoomUtils.broadcastMessage
Expand All @@ -28,7 +29,6 @@ import com.yuroyami.syncplay.utils.loggy
import com.yuroyami.syncplay.utils.timeStamper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

Expand All @@ -40,7 +40,8 @@ var viewmodel: SpViewModel? = null
class SpViewModel {
lateinit var p: SyncplayProtocol //If it is not initialized, it means we're in Solo Mode

val viewmodelScope = CoroutineScope(Dispatchers.IO)
val viewmodelScope = CoroutineScope(Dispatchers.Default)

var roomCallback: RoomCallback? = null

var player: BasePlayer? = null
Expand Down Expand Up @@ -69,6 +70,29 @@ class SpViewModel {

internal val isSoloMode: Boolean
get() = !::p.isInitialized

//TODO Lifecycle Stuff
var lifecycleWatchdog = object: LifecycleWatchdog {
override fun onResume() {

}

override fun onStop() {

}

override fun onCreate() {

}

override fun onStart() {

}

override fun onPause() {

}
}
}

/** Returns whether we're in Solo Mode, by checking if our protocol is initialized */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.uikit.ComposeUIViewControllerDelegate
import androidx.compose.ui.window.ComposeUIViewController
import com.yuroyami.syncplay.home.HomeCallback
import com.yuroyami.syncplay.home.HomeConfig
Expand Down Expand Up @@ -68,10 +69,17 @@ class AppleDelegate : NSObject(), UIApplicationDelegateProtocol {
return myOrientationMask
}


@Suppress("CONFLICTING_OVERLOADS")
override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {
(didFinishLaunchingWithOptions?.get(UIApplicationLaunchOptionsShortcutItemKey) as? UIApplicationShortcutItem)
?.let { handleShortcut(it)}
?.let { handleShortcut(it) }
return false
}

@Suppress("CONFLICTING_OVERLOADS")
override fun application(application: UIApplication, willFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {
(willFinishLaunchingWithOptions?.get(UIApplicationLaunchOptionsShortcutItemKey) as? UIApplicationShortcutItem)
?.let { handleShortcut(it) }
return false
}

Expand All @@ -87,7 +95,9 @@ val isRoom = mutableStateOf(false)
var sc: UIApplicationShortcutItem? = null

/** This view controller hosts and switches between room screen and home screen view controllers */
fun SyncplayController() = ComposeUIViewController {
fun SyncplayController() = ComposeUIViewController(configure = {
delegate = AppleLifecycleWatchdog
}) {
val room by remember { isRoom }

when (room) {
Expand Down Expand Up @@ -254,3 +264,28 @@ object Room : RoomCallback {
}
}
}


object AppleLifecycleWatchdog: ComposeUIViewControllerDelegate {
private val watchdog by lazy { viewmodel?.lifecycleWatchdog }

override fun viewDidAppear(animated: Boolean) {
watchdog?.onResume()
}

override fun viewDidDisappear(animated: Boolean) {
watchdog?.onStop()
}

override fun viewDidLoad() {
watchdog?.onCreate()
}

override fun viewWillAppear(animated: Boolean) {
watchdog?.onStart()
}

override fun viewWillDisappear(animated: Boolean) {
watchdog?.onPause()
}
}

0 comments on commit c42dea6

Please sign in to comment.