Skip to content

Merge Incoming, Outgoing, Call activities into a single one - #72

Merged
kanat merged 6 commits into
mainfrom
task/merge-activities
Nov 14, 2022
Merged

Merge Incoming, Outgoing, Call activities into a single one#72
kanat merged 6 commits into
mainfrom
task/merge-activities

Conversation

@kanat

@kanat kanat commented Nov 11, 2022

Copy link
Copy Markdown
Contributor

Closes: #38

# Conflicts:
#	app/src/main/kotlin/io/getstream/video/android/app/router/StreamRouterImpl.kt
#	app/src/main/kotlin/io/getstream/video/android/app/ui/call/CallActivity.kt
#	app/src/main/kotlin/io/getstream/video/android/app/ui/home/HomeActivity.kt
#	dogfooding/src/main/java/io/getstream/video/android/dogfooding/CallActivity.kt
#	dogfooding/src/main/java/io/getstream/video/android/dogfooding/DeeplinkingActivity.kt
#	dogfooding/src/main/java/io/getstream/video/android/dogfooding/HomeActivity.kt
#	stream-video-android/src/main/kotlin/io/getstream/video/android/router/StreamRouter.kt
#	stream-video-android/src/main/kotlin/io/getstream/video/android/viewmodel/CallViewModel.kt
#	stream-video-android/src/main/kotlin/io/getstream/video/android/viewmodel/IncomingCallViewModel.kt
#	stream-video-android/src/main/kotlin/io/getstream/video/android/viewmodel/OutgoingCallViewModel.kt
@kanat kanat linked an issue Nov 11, 2022 that may be closed by this pull request
@kanat
kanat marked this pull request as ready for review November 11, 2022 00:04
@kanat kanat changed the title Task/merge activities Merge Incoming, Outgoing, Call activities into a single one Nov 11, 2022

override fun onDestroy() {
streamVideo.removeSocketListener(socketListener)
logger.d { "[onDestroy] no args" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this then if we're not doing anything in onDestroy?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, left after debugging, thanks for catching this

}

override fun finish() {
logger.d { "[finish] no args" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

import io.getstream.video.android.viewmodel.CallViewModelFactory
import io.getstream.video.android.viewmodel.PermissionManagerImpl

public abstract class AbstractComposeCallActivity : AppCompatActivity(), StreamCallActivity {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be the recommended way of building our Call screen for customers?

We'll have to outline this in our tutorial if so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also - it mgith be better to rename StreamCallActivity to something else, as this seems like it implements two different set of activities (not possible in Android).

Maybe: StreamVideoProvider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, kinda lazy way for lazy people 馃榿

StreamVideoProvider - looks good 馃憣

if (isInitialized) return
callViewModel.connectToCall(
CallSettings(
audioOn = false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be a function that returns the call settings and users can override it to provide a different default:

protected open fun getDefaultCallSettings(): CallSettings {
    return CallSettings(false, true, false)
}

}

private fun StreamCallState.asTitle() = when (this) {
is StreamCallState.Drop -> "Drop"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use the class name here maybe? Or just expose a function in StreamCallState called: formatAsTitle that each sealed class member implements

onAcceptCall: () -> Unit,
onCancelCall: () -> Unit = {},
onMicToggleChanged: (Boolean) -> Unit = {},
onVideoToggleChanged: (Boolean) -> Unit,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that either all have default or none. Having a mix is confusing

Prefer all having default empty here

*/
public val callState: StateFlow<StreamCallState>

public fun launch(block: suspend CoroutineScope.() -> Unit)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was debugging the call flow and left it by mistake 馃檲

*/
override val callState: StateFlow<StreamCallState> = engine.callState

override fun launch(block: suspend CoroutineScope.() -> Unit) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this feels a bit weird to me - if we're going for Java interop or something, not sure if they can pass in a suspending lambda

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be removed

override suspend fun acceptCall(cid: String): Result<JoinedCall> {
logger.d { "[acceptCall] cid: $cid" }
val (type, id) = cid.split(":").apply {
if (size != 2) return Failure(VideoError(message = "invalid cid: $cid"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can pull these two functions out as utilities:

public fun isValidCid(cid: String): Boolean { // can also be an extension
    return cid.split(":").size == 2
}

public fun toIdAndType(cid: String): Pair<String, String> {
    return (maybe valid check here) cid.split(":").take(2)
}

logger.i { "[onSfuEvent] event: $event" }
}
when (event) {
is AudioLevelChangedEvent -> { }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to only handle the ones we care about and then just use:

else -> {}

As a default? I know that if we add more events it might be missed in the else branch, but this way it's just a bit ugly because we're effectively doing that but in a much longer way

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Engine is still something as WIP and we're supposed to support other events, so I just left the whole when options to see how many are left to support.
But we can replace it with else, I have no issues with that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with leaving it as-is until we reach a more "final" state

StreamLog.d(TAG) { "/start/ activity: ${input.className}" }
startActivity(
newIntent(input.className).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always need these flags? I imagine so because we're coming here from services

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the default behavior defined in DefaultCallAndroidInputLauncher, cause Activity can be launched from the appContext when PN arrives, for instance.

This can be easily overridden by providing a custom implementation of CallAndroidInputLauncher interface

public fun onUserLoggedIn()

public fun onOutgoingCall()
public fun onUserLoggedOut()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the router only know about login and logout now?

If that's the only thing it does, should be just remove the router altogether?

private val _callId: MutableStateFlow<String> = MutableStateFlow(value = "")
public val callId: StateFlow<String> = _callId

private val _participants: MutableStateFlow<List<CallUser>> = MutableStateFlow(emptyList())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have participantList, and now we also have CallUser - what is the difference/use case for these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it as it is for now, just merged what we had in all 3 flows (outgoing, incoming, in-call).

That's what I was thinking about as well, cause we have duplicate data in the following models:

  • CallUser
  • CallMember
  • CallParticipant

Maybe we could leave just CallUser & CallParticipant, what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that sounds good, the CallParticipant is when a user is in a call and has possible tracks published, whereas CallMember can be outside-of-the-call, but still a member

private val _participants: MutableStateFlow<List<CallUser>> = MutableStateFlow(emptyList())
public val participants: StateFlow<List<CallUser>> = _participants

private val _isMicrophoneEnabled = MutableStateFlow(false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be moved up to permissions and videoEnabled and such

is State.Outgoing -> {
_callType.value = CallType.fromType(state.callGuid.type)
_callId.value = state.callGuid.id
_participants.value = state.users.values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably call this something else then, not participants - as if we're in Incoming/Outgoing calls, we're not participanting yet, maybe members or invitees or idk

initializeCall(callSettings = callSettings)
viewModelScope.launch {
logger.d { "[connectToCall] state: ${streamCallState.value}" }
withTimeout(30_000) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull out as a constant

val state = streamCallState.first { it is State.InCall } as State.InCall
logger.v { "[connectToCall] received: ${streamCallState.value}" }
client = streamVideo.createCallClient(
state.callUrl.removeSuffix("/twirp"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally we'd not hardcode this but the BE should provide us with a sanitized URL, however we can maybe pull out as a util

@kanat kanat Nov 11, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this URL massaging, cause it was there before, so I just left it as it is =]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I added this bit since the BE returns a URL with an extra path suffix, and retrofit hates that

We can pull this out to a utils function maybe

this._isCameraEnabled.value = videoEnabled
}

private fun State.Outgoing.toMetadata(): CallMetadata =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be pulled out to a utility function rather than be placed here

@filbabic filbabic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some general comments on things I wasn't sure about.

There's one larger overarching thing:

  • We need KDocs absolutely everywhere we can: values/properties, private, internal nad public functions.

The codebase is getting larger and more complex, so it would be great to understand what's going on. Especially for new people.

We can use this branch for baseline and then branch off of it to work on other stuff, but I'd recommend writing KDocs as you go for everything you can (even if it seems repetitive), so that we have a nice and clean codebase that's well documented.

@kanat
kanat merged commit fb211ae into main Nov 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Merge Incoming, Outgoing, Call activities into a single one

2 participants