-
Notifications
You must be signed in to change notification settings - Fork 52
Publish appStateChanged/appMemoryLow events #677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Conversation
GlobalScope.launch { | ||
audioClient?.stopSession() | ||
appLifecycleObserver.stopObserving() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this seem like an odd place to put this? No higher level observer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is the highest level we can get, an alternative is to monitor the state change of DefaultAudioClientController.state
, but I don't like it.
@@ -570,8 +572,10 @@ class DefaultAudioClientObserver( | |||
|
|||
private fun handleAudioClientStop(statusCode: MeetingSessionStatusCode?) { | |||
if (audioClient != null) { | |||
// TODO: assess if only notifyAudioClientObserver should be in GlobalScope.launch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious about this, I think GlobalScope is a reasonable default, though it would be nice to allow configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with GlobalScope, what I mean is we should only have the notifyAudioClientObserver
logic in the GlobalScope, the rest of the logic should stay on the same thread as where handleAudioClientStop
is called, so that we can reduce the async overheads, but I need to think about it before making the change.
The overall code should look like this:
private fun handleAudioClientStop(statusCode: MeetingSessionStatusCode?) {
if (audioClient != null) {
audioClient?.stopSession()
appLifecycleObserver.stopObserving()
DefaultAudioClientController.audioClientState = AudioClientState.STOPPED
GlobalScope.launch {
notifyAudioClientObserver { observer ->
observer.onAudioSessionStopped(MeetingSessionStatus(statusCode))
}
}
} else {
logger.error(TAG, "Failed to stop audio session since audioClient is null")
}
}
import com.amazonaws.services.chime.sdk.meetings.analytics.MeetingHistoryEventName | ||
import com.amazonaws.services.chime.sdk.meetings.utils.logger.Logger | ||
|
||
class DefaultAppLifecycleObserver( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be Monitor
to differentiate from observer pattern?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I don't have a preference.
class DefaultAppLifecycleObserver( | ||
private val eventAnalyticsController: EventAnalyticsController, | ||
private val logger: Logger | ||
) : AppLifecycleObserver, DefaultLifecycleObserver { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we subscribe to other observers as well? https://developer.android.com/reference/androidx/lifecycle/DefaultLifecycleObserver
We could technically make this just one event type + attribute as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
private var eventAnalyticsObservers = ConcurrentSet.createConcurrentSet<EventAnalyticsObserver>() | ||
|
||
override fun publishEvent(name: EventName, attributes: EventAttributes?) { | ||
override fun publishEvent(name: EventName, attributes: EventAttributes?, notifyObservers: Boolean) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious, why is this optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you mean attributes:EventAttributes?
, my understanding is because not every event comes with attributes. Alternatively we can also use non-optional EventAttributes
and set to empty map for default, I'm not sure why we didn't go this approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant notifyObservers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently there are publishEvent()
and pushEvent()
in DefaultEventAnalyticsController
, the only diff between them is that pushEvent()
does not notify event observers, the rest of the logic is the same, so i'm tying to remove pushEvent()
, and use publishEvent()
for all events, adding notifyObservers
in order to make publishEvent()
have the same pattern as pushEvent()
.
@@ -16,7 +16,7 @@ interface EventAnalyticsController { | |||
* @param name: [EventName] - Name of event to publish | |||
* @param attributes: [EventAttributes] - Attributes of event to pass to builders. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs update, and if so probably explanation of why this may be false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch!
package com.amazonaws.services.chime.sdk.meetings.ingestion | ||
|
||
interface AppLifecycleObserver { | ||
fun startObserving() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets make these start() and stop(). Having the word Observing in the API name sounds redundant.
a93b6fb
to
b3b289c
Compare
eventReporter?.report(SDKEvent(name, eventAttributes)) | ||
|
||
ObserverUtils.notifyObserverOnMainThread(eventAnalyticsObservers) { | ||
it.onEventReceived(name, eventAttributes) | ||
if (notifyObservers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for making notification optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the first step to replace pushHistory()
with publishEvent()
- the only different between those 2 functions is publishHistory()
does not notify event observers, so we should merge those 2 functions(at least merge the logic if we still want to keep the signature). Having notification as optional will make the logic same(or closer) to pushHistory()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I missed the discussion, do we deprecate pushHistory? Why do we want to merge them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no discussion, this is just a change I'm planning to make.
Why do we want to merge them?
Because those functions are doing almost the same thing except notifying the event observers, it's causing confusions and not necessary to have duplicate logics.
@@ -15,8 +15,9 @@ interface EventAnalyticsController { | |||
* | |||
* @param name: [EventName] - Name of event to publish | |||
* @param attributes: [EventAttributes] - Attributes of event to pass to builders. | |||
* @param notifyObservers: [Boolean] - Whether to notify `EventAnalyticsObserver` of the events. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll prefer the explanation be more use case oriented, i.e. - when am I recommended to notify EventAnalyticsObserver and when not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do
|
||
private var handler: AppStateHandler? = null | ||
|
||
// App states should be posted only when the meeting session is running |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: meeting session isn't the hard requirement right? builder still can post events when use this monitor alone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a privacy concern, IMO it makes sense to monitor the states only when the session is running, if we monitor the app states all the time regardless if the session is running, it could lead to customer concerns. Builders can do whatever they want since they own the app.
appStateMonitor.start() | ||
appStateMonitor.start() | ||
|
||
verify(exactly = 2) { mockLogger.info(any(), "Started monitoring app state and memory") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These idempotent test cases are not how I exactly imagine - I think it should verify something only happen once even you call the API multiple time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And wondering if any alternative for verification method instead of matching texts in log which is relatively less stable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These idempotent test cases are not how I exactly imagine - I think it should verify something only happen once even you call the API multiple time.
- I think idempotent means it will have the effect as it performed once when performed multiple times, it doesn't have to happen once, as long as the result is the same. In the
start()
logic, we are resetting the observers before subscribing, so the logger will be called twice.
And wondering if any alternative for verification method instead of matching texts in log which is relatively less stable.
- I would say it's a more accurate verification by matching the texts, instead of
less stable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree "I think idempotent means it will have the effect as it performed once when performed multiple times", and that's why I feel the name is bit misleading to me, the effect is now 2 logs instead of 1 log. It is idempotent to me if only 1 log for multiple calls that prevent additional resources to be created. I'm good with naming like "start should resetting observers".
Stable here I meant if I update the log text, the test would fail unnecessarily, essentially the logic here isn't relying on the logging output but the API invocation. Ideally we could verify memoryHandler.post get called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense, will update!
|`contentShareSignalingDropped` |The content share WebSocket failed or closed with an error. | ||
|`videoClientSignalingDropped` |The video client signaling websocket failed or closed with an error. | ||
|`contentShareSignalingDropped` |The content share client signaling websocket failed or closed with an error. | ||
|`appStateChanged` |The application state is changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to update. for detail attributes for the states?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We added appState
attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant do we need to list what are the available states?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do
ℹ️ Description
Issue #, if available
Type of change
🧪 How Has This Been Tested?
Unit test coverage
Additional Manual Test
📱 Screenshots, if available
provide screenshots/video record if there's a UI change in demo app
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.