From c2c495c49df8e02c9939ba551a719c34b7cec2e2 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Nov 2021 15:08:02 +0000 Subject: [PATCH 1/5] keeping device screen on during any voice playback/recording within the room details activity - Fixes playback and recording stopping due to device inactivity - Adds a separate activity listener for monitoring if any audio is playing or being recording and keeping the screen on if so --- .../im/vector/app/core/extensions/Activity.kt | 9 +++++++++ .../home/room/detail/RoomDetailActivity.kt | 15 +++++++++++++++ .../helper/VoiceMessagePlaybackTracker.kt | 15 +++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt index de469b9e3a..da58498238 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt @@ -19,6 +19,7 @@ package im.vector.app.core.extensions import android.app.Activity import android.content.Intent import android.os.Parcelable +import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher @@ -112,3 +113,11 @@ fun Activity.restart() { startActivity(intent) finish() } + +fun Activity.keepScreenOn() { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) +} + +fun Activity.endKeepScreenOn() { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 415ca7bc04..2588f99861 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -30,12 +30,15 @@ import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.endKeepScreenOn import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.keepScreenOn import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityRoomDetailBinding import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment +import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker import im.vector.app.features.matrixto.MatrixToBottomSheet import im.vector.app.features.navigation.Navigator import im.vector.app.features.room.RequireActiveMembershipAction @@ -43,6 +46,9 @@ import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + +private const val ROOM_DETAILS_SCREEN_ON_TRACKER = "room_details_screen_on" @AndroidEntryPoint class RoomDetailActivity : @@ -73,6 +79,7 @@ class RoomDetailActivity : override fun getCoordinatorLayout() = views.coordinatorLayout + @Inject lateinit var playbackTracker: VoiceMessagePlaybackTracker private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel() @@ -114,6 +121,13 @@ class RoomDetailActivity : } } views.drawerLayout.addDrawerListener(drawerListener) + + playbackTracker.trackActivity(ROOM_DETAILS_SCREEN_ON_TRACKER) { isActive -> + when (isActive) { + true -> keepScreenOn() + false -> endKeepScreenOn() + } + } } private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) { @@ -136,6 +150,7 @@ class RoomDetailActivity : override fun onDestroy() { supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) views.drawerLayout.removeDrawerListener(drawerListener) + playbackTracker.unTrackActivity(ROOM_DETAILS_SCREEN_ON_TRACKER) super.onDestroy() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt index 2e8f6d9336..cf00c3e636 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt @@ -26,8 +26,17 @@ class VoiceMessagePlaybackTracker @Inject constructor() { private val mainHandler = Handler(Looper.getMainLooper()) private val listeners = mutableMapOf() + private val activityListeners = mutableMapOf() private val states = mutableMapOf() + fun trackActivity(key: String, listener: ActivityListener) { + activityListeners[key] = listener + } + + fun unTrackActivity(id: String) { + activityListeners.remove(id) + } + fun track(id: String, listener: Listener) { listeners[id] = listener @@ -52,8 +61,10 @@ class VoiceMessagePlaybackTracker @Inject constructor() { */ private fun setState(key: String, state: Listener.State) { states[key] = state + val isActive = states.values.any { it is Listener.State.Playing || it is Listener.State.Recording } mainHandler.post { listeners[key]?.onUpdate(state) + activityListeners.forEach { it.value.onUpdate(isActive) } } } @@ -125,4 +136,8 @@ class VoiceMessagePlaybackTracker @Inject constructor() { data class Recording(val amplitudeList: List) : State() } } + + fun interface ActivityListener { + fun onUpdate(isActive: Boolean) + } } From 6d2dc20ad1278702082510961c501c46a6d41dbc Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 24 Nov 2021 15:14:13 +0000 Subject: [PATCH 2/5] adding changelog entry --- changelog.d/4022.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4022.bugfix diff --git a/changelog.d/4022.bugfix b/changelog.d/4022.bugfix new file mode 100644 index 0000000000..517926e018 --- /dev/null +++ b/changelog.d/4022.bugfix @@ -0,0 +1 @@ +Keeping device screen on whilst recording and playing back voice messages \ No newline at end of file From 28af0a637fb5d0fa1b77ba741c306cfeb9f7bcea Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 25 Nov 2021 09:48:14 +0000 Subject: [PATCH 3/5] using instance based playback activity listener and updating param name to give more context --- .../home/room/detail/RoomDetailActivity.kt | 18 +++++++++--------- .../helper/VoiceMessagePlaybackTracker.kt | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 2588f99861..376debc700 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -48,8 +48,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import javax.inject.Inject -private const val ROOM_DETAILS_SCREEN_ON_TRACKER = "room_details_screen_on" - @AndroidEntryPoint class RoomDetailActivity : VectorBaseActivity(), @@ -77,6 +75,13 @@ class RoomDetailActivity : } } + private val playbackActivityListener = VoiceMessagePlaybackTracker.ActivityListener { isPlayingOrRecording -> + when (isPlayingOrRecording) { + true -> keepScreenOn() + false -> endKeepScreenOn() + } + } + override fun getCoordinatorLayout() = views.coordinatorLayout @Inject lateinit var playbackTracker: VoiceMessagePlaybackTracker @@ -122,12 +127,7 @@ class RoomDetailActivity : } views.drawerLayout.addDrawerListener(drawerListener) - playbackTracker.trackActivity(ROOM_DETAILS_SCREEN_ON_TRACKER) { isActive -> - when (isActive) { - true -> keepScreenOn() - false -> endKeepScreenOn() - } - } + playbackTracker.trackActivity(playbackActivityListener) } private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) { @@ -150,7 +150,7 @@ class RoomDetailActivity : override fun onDestroy() { supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) views.drawerLayout.removeDrawerListener(drawerListener) - playbackTracker.unTrackActivity(ROOM_DETAILS_SCREEN_ON_TRACKER) + playbackTracker.unTrackActivity(playbackActivityListener) super.onDestroy() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt index cf00c3e636..6bcc018167 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt @@ -26,15 +26,15 @@ class VoiceMessagePlaybackTracker @Inject constructor() { private val mainHandler = Handler(Looper.getMainLooper()) private val listeners = mutableMapOf() - private val activityListeners = mutableMapOf() + private val activityListeners = mutableListOf() private val states = mutableMapOf() - fun trackActivity(key: String, listener: ActivityListener) { - activityListeners[key] = listener + fun trackActivity(listener: ActivityListener) { + activityListeners.add(listener) } - fun unTrackActivity(id: String) { - activityListeners.remove(id) + fun unTrackActivity(listener: ActivityListener) { + activityListeners.remove(listener) } fun track(id: String, listener: Listener) { @@ -61,10 +61,10 @@ class VoiceMessagePlaybackTracker @Inject constructor() { */ private fun setState(key: String, state: Listener.State) { states[key] = state - val isActive = states.values.any { it is Listener.State.Playing || it is Listener.State.Recording } + val isPlayingOrRecording = states.values.any { it is Listener.State.Playing || it is Listener.State.Recording } mainHandler.post { listeners[key]?.onUpdate(state) - activityListeners.forEach { it.value.onUpdate(isActive) } + activityListeners.forEach { it.onUpdate(isPlayingOrRecording) } } } @@ -138,6 +138,6 @@ class VoiceMessagePlaybackTracker @Inject constructor() { } fun interface ActivityListener { - fun onUpdate(isActive: Boolean) + fun onUpdate(isPlayingOrRecording: Boolean) } } From 9e9116df2f75eace6ef3ea076fb14db01260aca0 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 25 Nov 2021 14:49:11 +0000 Subject: [PATCH 4/5] marking the playback tracker as idle when the voice recording stops - fixes missing EndkeepScreenOn --- .../features/home/room/detail/composer/VoiceMessageHelper.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt index 5d351e843f..592bf31739 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt @@ -74,6 +74,7 @@ class VoiceMessageHelper @Inject constructor( voiceRecorder.stopRecord() voiceRecorder.getVoiceMessageFile() } + try { voiceMessageFile?.let { val outputFileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", it, "Voice message.${it.extension}") @@ -153,6 +154,7 @@ class VoiceMessageHelper @Inject constructor( } fun stopPlayback() { + playbackTracker.stopPlayback(VoiceMessagePlaybackTracker.RECORDING_ID) mediaPlayer?.stop() stopPlaybackTicker() } From 034725ef9125229606db88da3fac18a52cbfd78a Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 25 Nov 2021 14:49:31 +0000 Subject: [PATCH 5/5] ignoring duplicated state updates --- .../vector/app/features/home/room/detail/RoomDetailActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 376debc700..684c0010a5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -75,11 +75,14 @@ class RoomDetailActivity : } } + private var lastKnownPlayingOrRecordingState: Boolean? = null private val playbackActivityListener = VoiceMessagePlaybackTracker.ActivityListener { isPlayingOrRecording -> + if (lastKnownPlayingOrRecordingState == isPlayingOrRecording) return@ActivityListener when (isPlayingOrRecording) { true -> keepScreenOn() false -> endKeepScreenOn() } + lastKnownPlayingOrRecordingState = isPlayingOrRecording } override fun getCoordinatorLayout() = views.coordinatorLayout