Skip to content
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

Show a banner in timeline while location sharing service is running #5660

Merged
merged 4 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 changelog.d/5660.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show a banner in timeline while location sharing service is running
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,6 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents()
object StopChatEffects : RoomDetailViewEvents()
object RoomReplacementStarted : RoomDetailViewEvents()

data class ChangeLocationIndicator(val isVisible: Boolean) : RoomDetailViewEvents()
}
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ class TimelineFragment @Inject constructor(
RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects()
is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it)
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
is RoomDetailViewEvents.ChangeLocationIndicator -> handleChangeLocationIndicator(it)
}
}

Expand Down Expand Up @@ -616,6 +617,10 @@ class TimelineFragment @Inject constructor(
)
}

private fun handleChangeLocationIndicator(event: RoomDetailViewEvents.ChangeLocationIndicator) {
views.locationLiveStatusIndicator.isVisible = event.isVisible
}

private fun displayErrorMessage(error: RoomDetailViewEvents.Failure) {
if (error.showInDialog) displayErrorDialog(error.throwable) else showErrorInSnackbar(error.throwable)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandle
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.typing.TypingHelper
import im.vector.app.features.location.LocationSharingServiceConnection
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
import im.vector.app.features.session.coroutineScope
Expand Down Expand Up @@ -124,10 +125,11 @@ class TimelineViewModel @AssistedInject constructor(
private val activeConferenceHolder: JitsiActiveConferenceHolder,
private val decryptionFailureTracker: DecryptionFailureTracker,
private val notificationDrawerManager: NotificationDrawerManager,
private val locationSharingServiceConnection: LocationSharingServiceConnection,
timelineFactory: TimelineFactory,
appStateHandler: AppStateHandler
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {

private val room = session.getRoom(initialState.roomId)!!
private val eventId = initialState.eventId
Expand Down Expand Up @@ -219,6 +221,9 @@ class TimelineViewModel @AssistedInject constructor(

// Threads
initThreads()

// Observe location service lifecycle to be able to warn the user
locationSharingServiceConnection.bind(this)
}

/**
Expand Down Expand Up @@ -1218,6 +1223,16 @@ class TimelineViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.OnNewTimelineEvents(eventIds))
}

override fun onLocationServiceRunning() {
_viewEvents.post(RoomDetailViewEvents.ChangeLocationIndicator(isVisible = true))
}

override fun onLocationServiceStopped() {
_viewEvents.post(RoomDetailViewEvents.ChangeLocationIndicator(isVisible = false))
// Bind again in case user decides to share live location without leaving the room
locationSharingServiceConnection.bind(this)
}

override fun onCleared() {
timeline.dispose()
timeline.removeAllListeners()
Expand All @@ -1231,6 +1246,7 @@ class TimelineViewModel @AssistedInject constructor(
// we should also mark it as read here, for the scenario that the user
// is already in the thread timeline
markThreadTimelineAsReadLocal()
locationSharingServiceConnection.unbind()
super.onCleared()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package im.vector.app.features.location

import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.os.Parcelable
import dagger.hilt.android.AndroidEntryPoint
Expand All @@ -41,6 +42,8 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var locationTracker: LocationTracker

private val binder = LocalBinder()

private var roomArgsList = mutableListOf<RoomArgs>()
private var timers = mutableListOf<Timer>()

Expand Down Expand Up @@ -120,8 +123,12 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
destroyMe()
}

override fun onBind(intent: Intent?): IBinder? {
return null
override fun onBind(intent: Intent?): IBinder {
return binder
}

inner class LocalBinder : Binder() {
fun getService(): LocationSharingService = this@LocationSharingService
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.app.features.location

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import javax.inject.Inject

class LocationSharingServiceConnection @Inject constructor(
private val context: Context
) : ServiceConnection {

interface Callback {
fun onLocationServiceRunning()
fun onLocationServiceStopped()
}

private var callback: Callback? = null
private var isBound = false

fun bind(callback: Callback) {
this.callback = callback

if (isBound) {
callback.onLocationServiceRunning()
} else {
Intent(context, LocationSharingService::class.java).also { intent ->
context.bindService(intent, this, 0)
}
}
}

fun unbind() {
callback = null
}

override fun onServiceConnected(className: ComponentName, binder: IBinder) {
isBound = true
callback?.onLocationServiceRunning()
}

override fun onServiceDisconnected(className: ComponentName) {
isBound = false
callback?.onLocationServiceStopped()
}
}