diff --git a/app/src/main/java/com/quran/labs/androidquran/bridge/AudioEventPresenterBridge.kt b/app/src/main/java/com/quran/labs/androidquran/bridge/AudioEventPresenterBridge.kt deleted file mode 100644 index ea3d929f78..0000000000 --- a/app/src/main/java/com/quran/labs/androidquran/bridge/AudioEventPresenterBridge.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.quran.labs.androidquran.bridge - -import com.quran.data.model.SuraAyah -import com.quran.reading.common.AudioEventPresenter -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - -class AudioEventPresenterBridge constructor( - audioEventPresenter: AudioEventPresenter, - onPlaybackAyahChanged: ((SuraAyah?) -> Unit) -) { - - private val scope = MainScope() - private val audioPlaybackAyahFlow = audioEventPresenter.audioPlaybackAyahFlow - - init { - audioPlaybackAyahFlow - .onEach { onPlaybackAyahChanged(it) } - .launchIn(scope) - } - - fun dispose() { - scope.cancel() - } -} diff --git a/app/src/main/java/com/quran/labs/androidquran/bridge/AudioStatusRepositoryBridge.kt b/app/src/main/java/com/quran/labs/androidquran/bridge/AudioStatusRepositoryBridge.kt new file mode 100644 index 0000000000..01e150c515 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/bridge/AudioStatusRepositoryBridge.kt @@ -0,0 +1,60 @@ +package com.quran.labs.androidquran.bridge + +import com.quran.data.model.SuraAyah +import com.quran.labs.androidquran.common.audio.model.playback.AudioRequest +import com.quran.labs.androidquran.common.audio.model.playback.AudioStatus +import com.quran.labs.androidquran.common.audio.model.playback.PlaybackStatus +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository +import com.quran.labs.androidquran.view.AudioStatusBar +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +class AudioStatusRepositoryBridge( + audioStatusRepository: AudioStatusRepository, + audioStatusBar: () -> AudioStatusBar, + onPlaybackAyahChanged: ((SuraAyah?) -> Unit) +) { + + private val scope = MainScope() + private val audioPlaybackAyahFlow = audioStatusRepository.audioPlaybackFlow + + init { + audioPlaybackAyahFlow + .onEach { status -> + when (status) { + is AudioStatus.Playback -> { + val statusBar = audioStatusBar() + if (status.playbackStatus == PlaybackStatus.PLAYING) { + statusBar.switchMode(AudioStatusBar.PLAYING_MODE) + if (status.audioRequest.repeatInfo >= -1) { + statusBar.setRepeatCount(status.audioRequest.repeatInfo) + statusBar.setSpeed(status.audioRequest.playbackSpeed) + } + } else if (status.playbackStatus == PlaybackStatus.PAUSED) { + statusBar.switchMode(AudioStatusBar.PAUSED_MODE) + } else if (status.playbackStatus == PlaybackStatus.PREPARING) { + statusBar.switchMode(AudioStatusBar.LOADING_MODE) + } + onPlaybackAyahChanged(status.currentAyah) + } + AudioStatus.Stopped -> { + audioStatusBar().switchMode(AudioStatusBar.STOPPED_MODE) + } + } + } + .launchIn(scope) + } + + fun audioRequest(): AudioRequest? { + return when (val status = audioPlaybackAyahFlow.value) { + is AudioStatus.Playback -> status.audioRequest + AudioStatus.Stopped -> null + } + } + + fun dispose() { + scope.cancel() + } +} diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/audio/service/AudioQueue.kt b/app/src/main/java/com/quran/labs/androidquran/presenter/audio/service/AudioQueue.kt index 4584911e52..5feface706 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/audio/service/AudioQueue.kt +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/audio/service/AudioQueue.kt @@ -55,6 +55,7 @@ class AudioQueue(private val quranInfo: QuranInfo, fun getCurrentSura() = playbackInfo.currentAyah.sura fun getCurrentAyah() = playbackInfo.currentAyah.ayah + fun getCurrentPlaybackAyah() = playbackInfo.currentAyah fun playNextAyah(skipAyahRepeat: Boolean = false): Boolean { if (playbackInfo.shouldPlayBasmallah) { diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/quran/ayahtracker/AyahTrackerPresenter.kt b/app/src/main/java/com/quran/labs/androidquran/presenter/quran/ayahtracker/AyahTrackerPresenter.kt index 5b19973c31..ede00ac03f 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/quran/ayahtracker/AyahTrackerPresenter.kt +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/quran/ayahtracker/AyahTrackerPresenter.kt @@ -17,6 +17,8 @@ import com.quran.data.model.selection.AyahSelection import com.quran.data.model.selection.SelectionIndicator import com.quran.data.model.selection.startSuraAyah import com.quran.labs.androidquran.common.QuranAyahInfo +import com.quran.labs.androidquran.common.audio.model.playback.currentPlaybackAyah +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository import com.quran.labs.androidquran.data.QuranDisplayData import com.quran.labs.androidquran.data.SuraAyahIterator import com.quran.labs.androidquran.presenter.Presenter @@ -34,7 +36,6 @@ import com.quran.mobile.bookmark.model.BookmarkModel import com.quran.mobile.translation.model.LocalTranslation import com.quran.page.common.data.AyahCoordinates import com.quran.page.common.data.PageCoordinates -import com.quran.reading.common.AudioEventPresenter import com.quran.reading.common.ReadingEventPresenter import com.quran.recitation.events.RecitationEventPresenter import com.quran.recitation.presenter.RecitationHighlightsPresenter @@ -58,8 +59,8 @@ class AyahTrackerPresenter @Inject constructor( private val quranSettings: QuranSettings, private val readingEventPresenter: ReadingEventPresenter, private val bookmarkModel: BookmarkModel, - private val audioEventPresenter: AudioEventPresenter, - private val recitationPresenter: RecitationPresenter, + private val audioStatusRepository: AudioStatusRepository, + recitationPresenter: RecitationPresenter, private val recitationEventPresenter: RecitationEventPresenter, private val recitationPopupPresenter: RecitationPopupPresenter, private val recitationHighlightsPresenter: RecitationHighlightsPresenter, @@ -81,8 +82,8 @@ class AyahTrackerPresenter @Inject constructor( .onEach { onAyahSelectionChanged(it) } .launchIn(scope) - audioEventPresenter.audioPlaybackAyahFlow - .onEach { onAudioSelectionChanged(it) } + audioStatusRepository.audioPlaybackFlow + .onEach { onAudioSelectionChanged(it.currentPlaybackAyah()) } .launchIn(scope) items.forEach { trackerItem -> diff --git a/app/src/main/java/com/quran/labs/androidquran/service/AudioService.kt b/app/src/main/java/com/quran/labs/androidquran/service/AudioService.kt index ef1495bc2a..fbf812b05b 100644 --- a/app/src/main/java/com/quran/labs/androidquran/service/AudioService.kt +++ b/app/src/main/java/com/quran/labs/androidquran/service/AudioService.kt @@ -56,14 +56,15 @@ import android.util.SparseIntArray import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.core.math.MathUtils.clamp -import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.media.session.MediaButtonReceiver import com.quran.data.core.QuranInfo -import com.quran.data.model.SuraAyah import com.quran.labs.androidquran.QuranApplication import com.quran.labs.androidquran.R -import com.quran.labs.androidquran.dao.audio.AudioPlaybackInfo import com.quran.labs.androidquran.common.audio.model.playback.AudioRequest +import com.quran.labs.androidquran.common.audio.model.playback.AudioStatus +import com.quran.labs.androidquran.common.audio.model.playback.PlaybackStatus +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository +import com.quran.labs.androidquran.dao.audio.AudioPlaybackInfo import com.quran.labs.androidquran.data.Constants import com.quran.labs.androidquran.data.QuranDisplayData import com.quran.labs.androidquran.data.QuranFileConstants @@ -77,7 +78,6 @@ import com.quran.labs.androidquran.service.util.QuranDownloadNotifier import com.quran.labs.androidquran.ui.PagerActivity import com.quran.labs.androidquran.util.AudioUtils import com.quran.labs.androidquran.util.NotificationChannelUtil.setupNotificationChannel -import com.quran.reading.common.AudioEventPresenter import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Maybe import io.reactivex.rxjava3.core.Single @@ -98,17 +98,6 @@ import kotlin.math.abs */ class AudioService : Service(), OnCompletionListener, OnPreparedListener, MediaPlayer.OnErrorListener, AudioFocusable, OnSeekCompleteListener { - object AudioUpdateIntent { - const val INTENT_NAME = "com.quran.labs.androidquran.audio.AudioUpdate" - const val STATUS = "status" - const val SURA = "sura" - const val AYAH = "ayah" - const val REPEAT_COUNT = "repeat_count" - const val REQUEST = "request" - const val STOPPED = 0 - const val PLAYING = 1 - const val PAUSED = 2 - } // our media player private var player: MediaPlayer? = null @@ -162,7 +151,6 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, private lateinit var audioFocusHelper: AudioFocusHelper private lateinit var notificationManager: NotificationManager - private lateinit var broadcastManager: LocalBroadcastManager private lateinit var noisyAudioStreamReceiver: BroadcastReceiver private lateinit var mediaSession: MediaSessionCompat @@ -193,7 +181,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, lateinit var audioUtils: AudioUtils @Inject - lateinit var audioEventPresenter: AudioEventPresenter + lateinit var audioStatusRepository: AudioStatusRepository private inner class ServiceHandler(looper: Looper) : Handler(looper) { override fun handleMessage(msg: Message) { @@ -262,7 +250,6 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, // create the Audio Focus Helper audioFocusHelper = AudioFocusHelper(appContext, this) - broadcastManager = LocalBroadcastManager.getInstance(appContext) noisyAudioStreamReceiver = NoisyAudioStreamReceiver() ContextCompat.registerReceiver( @@ -347,38 +334,14 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, if (State.Stopped == state) { processStopRequest(true) } else { - var sura = -1 - var ayah = -1 - var repeatCount = -200 - var state = AudioUpdateIntent.PLAYING - if (State.Paused == this.state) { - state = AudioUpdateIntent.PAUSED - } - - val localAudioQueue = audioQueue - val localAudioRequest = audioRequest - if (localAudioQueue != null && localAudioRequest != null) { - sura = localAudioQueue.getCurrentSura() - ayah = localAudioQueue.getCurrentAyah() - repeatCount = localAudioRequest.repeatInfo - } - - val updateIntent = Intent(AudioUpdateIntent.INTENT_NAME) - updateIntent.putExtra(AudioUpdateIntent.STATUS, state) - updateIntent.putExtra(AudioUpdateIntent.SURA, sura) - updateIntent.putExtra(AudioUpdateIntent.AYAH, ayah) - updateIntent.putExtra(AudioUpdateIntent.REPEAT_COUNT, repeatCount) - updateIntent.putExtra(AudioUpdateIntent.REQUEST,localAudioRequest) - broadcastManager.sendBroadcast(updateIntent) + updateAudioPlaybackStatus() } } else if (ACTION_PLAYBACK == action) { val updatedAudioRequest = intent.getParcelableExtra(EXTRA_PLAY_INFO) if (updatedAudioRequest != null) { audioRequest = updatedAudioRequest val start = updatedAudioRequest.start - audioEventPresenter.onAyahPlayback(start) - val basmallah = !updatedAudioRequest.isGapless() && - start.requiresBasmallah() + val basmallah = !updatedAudioRequest.isGapless() && start.requiresBasmallah() audioQueue = AudioQueue( quranInfo, updatedAudioRequest, AudioPlaybackInfo(start, 1, 1, basmallah) @@ -627,7 +590,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, setUpAsForeground() } configAndStartMediaPlayer(false) - notifyAudioStatus(AudioUpdateIntent.PLAYING) + updateAudioPlaybackStatus() } } @@ -642,11 +605,12 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, // update the notification. relaxResources(releaseMediaPlayer = false, stopForeground = false) pauseNotification() - notifyAudioStatus(AudioUpdateIntent.PAUSED) + updateAudioPlaybackStatus() } else if (State.Stopped == state) { // if we get a pause while we're already stopped, it means we likely woke up because // of AudioIntentReceiver, so just stop in this case. setState(PlaybackStateCompat.STATE_STOPPED) + updateAudioPlaybackStatus() stopSelf() } } @@ -664,6 +628,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, seekTo = getSeekPosition(true) pos -= seekTo } + if (pos > 1500 && !playerOverride) { localPlayer.seekTo(seekTo) state = State.Playing // in case we were paused @@ -739,6 +704,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } if (force || State.Stopped != state) { state = State.Stopped + updateAudioPlaybackStatus() // let go of all resources... relaxResources(releaseMediaPlayer = true, stopForeground = true) @@ -750,25 +716,13 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, // stop async task if it's running timingDisposable?.dispose() - - // tell the ui we've stopped - audioEventPresenter.onAyahPlayback(null) - notifyAudioStatus(AudioUpdateIntent.STOPPED) } } private fun notifyAyahChanged() { - val localAudioQueue = audioQueue ?: return val localAudioRequest = audioRequest ?: return + updateAudioPlaybackStatus() - audioEventPresenter.onAyahPlayback( - SuraAyah(localAudioQueue.getCurrentSura(), localAudioQueue.getCurrentAyah()) - ) - val updateIntent = Intent(AudioUpdateIntent.INTENT_NAME) - updateIntent.putExtra(AudioUpdateIntent.STATUS, AudioUpdateIntent.PLAYING) - updateIntent.putExtra(AudioUpdateIntent.SURA, localAudioQueue.getCurrentSura()) - updateIntent.putExtra(AudioUpdateIntent.AYAH, localAudioQueue.getCurrentAyah()) - broadcastManager.sendBroadcast(updateIntent) val metadataBuilder = MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, getTitle()) .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, localAudioRequest.qari.name) @@ -789,10 +743,30 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, mediaSession.setMetadata(metadataBuilder.build()) } - private fun notifyAudioStatus(status: Int) { - val updateIntent = Intent(AudioUpdateIntent.INTENT_NAME) - updateIntent.putExtra(AudioUpdateIntent.STATUS, status) - broadcastManager.sendBroadcast(updateIntent) + private fun updateAudioPlaybackStatus() { + val audioStatus = when (state) { + State.Stopped -> AudioStatus.Stopped + State.Playing, State.Preparing, State.Paused -> { + val localAudioQueue = audioQueue ?: return + val localAudioRequest = audioRequest ?: return + + AudioStatus.Playback( + localAudioQueue.getCurrentPlaybackAyah(), + localAudioRequest, + state.asPlayingPlaybackStatus() + ) + } + } + audioStatusRepository.updateAyahPlayback(audioStatus) + } + + private fun State.asPlayingPlaybackStatus(): PlaybackStatus { + return when (this) { + State.Playing -> PlaybackStatus.PLAYING + State.Preparing -> PlaybackStatus.PREPARING + State.Paused -> PlaybackStatus.PAUSED + else -> throw IllegalStateException("State $this is not a playing state") + } } /** @@ -879,6 +853,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, if (!player.isPlaying) { player.start() state = State.Playing + updateAudioPlaybackStatus() } return } @@ -902,6 +877,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } player.start() state = State.Playing + updateAudioPlaybackStatus() } } @@ -940,10 +916,6 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, val url = audioQueue?.getUrl() if (localAudioRequest == null || localAudioQueue == null || url == null) { - val updateIntent = Intent(AudioUpdateIntent.INTENT_NAME) - updateIntent.putExtra(AudioUpdateIntent.STATUS, AudioUpdateIntent.STOPPED) - audioEventPresenter.onAyahPlayback(null) - broadcastManager.sendBroadcast(updateIntent) processStopRequest(true) // stop everything! return } @@ -952,11 +924,6 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, if (!isStreaming) { val f = File(url) if (!f.exists()) { - val updateIntent = Intent(AudioUpdateIntent.INTENT_NAME) - updateIntent.putExtra(AudioUpdateIntent.STATUS, AudioUpdateIntent.STOPPED) - updateIntent.putExtra(EXTRA_PLAY_INFO, audioRequest) - audioEventPresenter.onAyahPlayback(null) - broadcastManager.sendBroadcast(updateIntent) processStopRequest(true) return } @@ -1010,6 +977,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, localPlayer.setDataSource(url) } state = State.Preparing + updateAudioPlaybackStatus() val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) @@ -1070,6 +1038,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, ) player.start() state = State.Playing + updateAudioPlaybackStatus() audioRequest?.playbackSpeed?.let { speed -> processUpdatePlaybackSpeed(speed) } @@ -1326,6 +1295,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { Timber.e("Error: what=%s, extra=%s", what.toString(), extra.toString()) state = State.Stopped + updateAudioPlaybackStatus() relaxResources(releaseMediaPlayer = true, stopForeground = true) giveUpAudioFocus() return true // true indicates we handled the error diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.java b/app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.java index f665f8c62e..b9681bbd9a 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.java @@ -7,7 +7,6 @@ import android.Manifest; import android.app.ProgressDialog; import android.app.SearchManager; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -65,11 +64,12 @@ import com.quran.labs.androidquran.QuranPreferenceActivity; import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.SearchActivity; -import com.quran.labs.androidquran.bridge.AudioEventPresenterBridge; +import com.quran.labs.androidquran.bridge.AudioStatusRepositoryBridge; import com.quran.labs.androidquran.bridge.ReadingEventPresenterBridge; import com.quran.labs.androidquran.common.QuranAyahInfo; import com.quran.labs.androidquran.common.audio.model.QariItem; import com.quran.labs.androidquran.common.audio.model.playback.AudioRequest; +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository; import com.quran.labs.androidquran.data.Constants; import com.quran.labs.androidquran.data.QuranDataProvider; import com.quran.labs.androidquran.data.QuranDisplayData; @@ -124,7 +124,6 @@ import com.quran.page.common.factory.PageViewFactoryProvider; import com.quran.page.common.toolbar.AyahToolBar; import com.quran.page.common.toolbar.di.AyahToolBarInjector; -import com.quran.reading.common.AudioEventPresenter; import com.quran.reading.common.ReadingEventPresenter; import java.lang.ref.WeakReference; @@ -189,7 +188,6 @@ public class PagerActivity extends AppCompatActivity implements private boolean needsPermissionToDownloadOver3g = true; private AlertDialog promptDialog = null; private AyahToolBar ayahToolBar; - private AudioRequest lastAudioRequest; private boolean isDualPages = false; private View toolBarArea; private FrameLayout overlay; @@ -238,14 +236,14 @@ public class PagerActivity extends AppCompatActivity implements @Inject AudioPresenter audioPresenter; @Inject CurrentQariBridge currentQariBridge; @Inject QuranEventLogger quranEventLogger; - @Inject AudioEventPresenter audioEventPresenter; + @Inject AudioStatusRepository audioStatusRepository; @Inject ReadingEventPresenter readingEventPresenter; @Inject PageViewFactoryProvider pageProviderFactoryProvider; @Inject Set additionalAyahPanels; @Inject PagerActivityRecitationPresenter pagerActivityRecitationPresenter; @Inject TranslationListPresenter translationListPresenter; - private AudioEventPresenterBridge audioEventPresenterBridge; + private AudioStatusRepositoryBridge audioStatusRepositoryBridge; private ReadingEventPresenterBridge readingEventPresenterBridge; private Job translationJob; @@ -288,8 +286,9 @@ public void onCreate(Bundle savedInstanceState) { boolean shouldAdjustPageNumber = false; isDualPages = QuranUtils.isDualPages(this, quranScreenInfo); isSplitScreen = quranSettings.isQuranSplitWithTranslation(); - audioEventPresenterBridge = new AudioEventPresenterBridge( - audioEventPresenter, + audioStatusRepositoryBridge = new AudioStatusRepositoryBridge( + audioStatusRepository, + () -> audioStatusBar, suraAyah -> { onAudioPlaybackAyahChanged(suraAyah); return null; } ); readingEventPresenterBridge = new ReadingEventPresenterBridge( @@ -315,7 +314,6 @@ public void onCreate(Bundle savedInstanceState) { } boolean lastWasDualPages = savedInstanceState.getBoolean(LAST_WAS_DUAL_PAGES, isDualPages); shouldAdjustPageNumber = (lastWasDualPages != isDualPages); - this.lastAudioRequest = savedInstanceState.getParcelable(LAST_AUDIO_REQUEST); } else { Intent intent = getIntent(); Bundle extras = intent.getExtras(); @@ -510,10 +508,6 @@ public void onPageSelected(int position) { } } - LocalBroadcastManager.getInstance(this).registerReceiver( - audioReceiver, - new IntentFilter(AudioService.AudioUpdateIntent.INTENT_NAME)); - downloadReceiver = new DefaultDownloadReceiver(this, QuranDownloadService.DOWNLOAD_TYPE_AUDIO); String action = QuranDownloadNotifier.ProgressIntent.INTENT_NAME; @@ -986,7 +980,6 @@ protected void onDestroy() { clearUiVisibilityListener(); // remove broadcast receivers - LocalBroadcastManager.getInstance(this).unregisterReceiver(audioReceiver); if (downloadReceiver != null) { downloadReceiver.setListener(null); LocalBroadcastManager.getInstance(this) @@ -999,7 +992,7 @@ protected void onDestroy() { } currentQariBridge.unsubscribeAll(); compositeDisposable.dispose(); - audioEventPresenterBridge.dispose(); + audioStatusRepositoryBridge.dispose(); readingEventPresenterBridge.dispose(); handler.removeCallbacksAndMessages(null); dismissProgressDialog(); @@ -1017,9 +1010,6 @@ public void onSaveInstanceState(Bundle state) { state.putBoolean(LAST_READING_MODE_IS_TRANSLATION, showingTranslation); state.putBoolean(LAST_ACTIONBAR_STATE, isActionBarHidden); state.putBoolean(LAST_WAS_DUAL_PAGES, isDualPages); - if (lastAudioRequest != null) { - state.putParcelable(LAST_AUDIO_REQUEST, lastAudioRequest); - } super.onSaveInstanceState(state); } @@ -1275,33 +1265,6 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { } } - private final BroadcastReceiver audioReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent != null) { - int state = intent.getIntExtra( - AudioService.AudioUpdateIntent.STATUS, -1); - int repeatCount = intent.getIntExtra( - AudioService.AudioUpdateIntent.REPEAT_COUNT, -200); - AudioRequest request = intent.getParcelableExtra(AudioService.AudioUpdateIntent.REQUEST); - if (request != null) { - lastAudioRequest = request; - } - if (state == AudioService.AudioUpdateIntent.PLAYING) { - audioStatusBar.switchMode(AudioStatusBar.PLAYING_MODE); - if (repeatCount >= -1) { - audioStatusBar.setRepeatCount(repeatCount); - } - } else if (state == AudioService.AudioUpdateIntent.PAUSED) { - audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); - } else if (state == AudioService.AudioUpdateIntent.STOPPED) { - audioStatusBar.switchMode(AudioStatusBar.STOPPED_MODE); - lastAudioRequest = null; - } - } - } - }; - @Override public void updateDownloadProgress(int progress, long downloadedSize, long totalSize) { @@ -1560,10 +1523,6 @@ public void handlePlayback(AudioRequest request) { intent.setAction(AudioService.ACTION_PLAYBACK); if (request != null) { intent.putExtra(AudioService.EXTRA_PLAY_INFO, request); - lastAudioRequest = request; - audioStatusBar.setRepeatCount(request.getRepeatInfo()); - audioStatusBar.setSpeed(request.getPlaybackSpeed()); - audioStatusBar.switchMode(AudioStatusBar.LOADING_MODE); } Timber.d("starting service for audio playback"); @@ -1579,6 +1538,7 @@ public void onPausePressed() { @Override public void setPlaybackSpeed(float speed) { + final AudioRequest lastAudioRequest = audioStatusRepositoryBridge.audioRequest(); if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1594,7 +1554,6 @@ public void setPlaybackSpeed(float speed) { i.setAction(AudioService.ACTION_UPDATE_SETTINGS); i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); startService(i); - lastAudioRequest = updatedAudioRequest; } } @@ -1639,6 +1598,7 @@ public boolean updatePlayOptions(int rangeRepeat, int verseRepeat, boolean enforceRange, float playbackSpeed) { + final AudioRequest lastAudioRequest = audioStatusRepositoryBridge.audioRequest(); if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1653,10 +1613,6 @@ public boolean updatePlayOptions(int rangeRepeat, i.setAction(AudioService.ACTION_UPDATE_SETTINGS); i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); startService(i); - - lastAudioRequest = updatedAudioRequest; - audioStatusBar.setRepeatCount(verseRepeat); - audioStatusBar.setSpeed(playbackSpeed); return true; } else { return false; @@ -1665,6 +1621,7 @@ public boolean updatePlayOptions(int rangeRepeat, @Override public void setRepeatCount(int repeatCount) { + final AudioRequest lastAudioRequest = audioStatusRepositoryBridge.audioRequest(); if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1680,7 +1637,6 @@ public void setRepeatCount(int repeatCount) { i.setAction(AudioService.ACTION_UPDATE_SETTINGS); i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); startService(i); - lastAudioRequest = updatedAudioRequest; } } @@ -1688,7 +1644,6 @@ public void setRepeatCount(int repeatCount) { public void onStopPressed() { startService(audioUtils.getAudioIntent(this, AudioService.ACTION_STOP)); audioStatusBar.switchMode(AudioStatusBar.STOPPED_MODE); - lastAudioRequest = null; } @Override @@ -1740,7 +1695,7 @@ private SuraAyah getSelectionEnd() { } public AudioRequest getLastAudioRequest() { - return lastAudioRequest; + return audioStatusRepositoryBridge.audioRequest(); } public void endAyahMode() { diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahActionFragment.kt b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahActionFragment.kt index 0b7f45314f..0bd1a4f4f1 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahActionFragment.kt +++ b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahActionFragment.kt @@ -6,7 +6,8 @@ import com.quran.data.model.SuraAyah import com.quran.data.model.selection.AyahSelection import com.quran.data.model.selection.endSuraAyah import com.quran.data.model.selection.startSuraAyah -import com.quran.reading.common.AudioEventPresenter +import com.quran.labs.androidquran.common.audio.model.playback.currentPlaybackAyah +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository import com.quran.reading.common.ReadingEventPresenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope @@ -23,7 +24,7 @@ abstract class AyahActionFragment : Fragment() { lateinit var readingEventPresenter: ReadingEventPresenter @Inject - lateinit var audioEventPresenter: AudioEventPresenter + lateinit var audioStatusRepository: AudioStatusRepository protected var start: SuraAyah? = null protected var end: SuraAyah? = null @@ -33,7 +34,8 @@ abstract class AyahActionFragment : Fragment() { scope = MainScope() readingEventPresenter.ayahSelectionFlow - .combine(audioEventPresenter.audioPlaybackAyahFlow) { selectedAyah, playbackAyah -> + .combine(audioStatusRepository.audioPlaybackFlow) { selectedAyah, playbackStatus -> + val playbackAyah = playbackStatus.currentPlaybackAyah() val (previousStart, previousEnd) = start to end if (selectedAyah !is AyahSelection.None) { start = selectedAyah.startSuraAyah() diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/TagBookmarkFragment.kt b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/TagBookmarkFragment.kt index fc34229393..b680448b50 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/TagBookmarkFragment.kt +++ b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/TagBookmarkFragment.kt @@ -5,11 +5,12 @@ import android.os.Bundle import com.quran.data.core.QuranInfo import com.quran.data.model.selection.AyahSelection import com.quran.data.model.selection.startSuraAyah +import com.quran.labs.androidquran.common.audio.model.playback.currentPlaybackAyah +import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository import com.quran.labs.androidquran.common.toolbar.R import com.quran.labs.androidquran.ui.PagerActivity import com.quran.labs.androidquran.ui.helpers.SlidingPagerAdapter import com.quran.mobile.di.AyahActionFragmentProvider -import com.quran.reading.common.AudioEventPresenter import com.quran.reading.common.ReadingEventPresenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope @@ -25,7 +26,7 @@ class TagBookmarkFragment : TagBookmarkDialog() { lateinit var readingEventPresenter: ReadingEventPresenter @Inject - lateinit var audioEventPresenter: AudioEventPresenter + lateinit var audioStatusRepository: AudioStatusRepository @Inject lateinit var quranInfo: QuranInfo @@ -46,7 +47,8 @@ class TagBookmarkFragment : TagBookmarkDialog() { scope = MainScope() readingEventPresenter.ayahSelectionFlow - .combine(audioEventPresenter.audioPlaybackAyahFlow) { selectedAyah, playbackAyah -> + .combine(audioStatusRepository.audioPlaybackFlow) { selectedAyah, playbackState -> + val playbackAyah = playbackState.currentPlaybackAyah() val start = when { selectedAyah !is AyahSelection.None -> selectedAyah.startSuraAyah() playbackAyah != null -> playbackAyah diff --git a/app/src/main/java/com/quran/labs/androidquran/view/AudioStatusBar.java b/app/src/main/java/com/quran/labs/androidquran/view/AudioStatusBar.java index 053889af7f..3b939eaf02 100644 --- a/app/src/main/java/com/quran/labs/androidquran/view/AudioStatusBar.java +++ b/app/src/main/java/com/quran/labs/androidquran/view/AudioStatusBar.java @@ -261,7 +261,7 @@ private void showStoppedMode() { private void updateButton() { final TextView currentQariView = qariView; - if (currentQariView != null) { + if (currentQariView != null && currentQari != null) { currentQariView.setText(currentQari.getNameResource()); } } @@ -283,7 +283,7 @@ private void addButton() { dropdownIconView.setOnClickListener(view -> audioBarListener.onShowQariList()); dropdownIconView.setPadding(buttonPadding, 0, buttonPadding, 0); } - qariView.setText(currentQari.getNameResource()); + updateButton(); final ViewGroup.LayoutParams dropdownParams = new ViewGroup.LayoutParams( diff --git a/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/AudioStatus.kt b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/AudioStatus.kt new file mode 100644 index 0000000000..ba0e42b813 --- /dev/null +++ b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/AudioStatus.kt @@ -0,0 +1,15 @@ +package com.quran.labs.androidquran.common.audio.model.playback + +import com.quran.data.model.SuraAyah + +sealed class AudioStatus { + data object Stopped : AudioStatus() + data class Playback( + val currentAyah: SuraAyah, + val audioRequest: AudioRequest, + val playbackStatus: PlaybackStatus + ) : AudioStatus() +} + +fun AudioStatus.currentPlaybackAyah() = + (this as? AudioStatus.Playback)?.currentAyah diff --git a/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/PlaybackStatus.kt b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/PlaybackStatus.kt new file mode 100644 index 0000000000..72d3bb37be --- /dev/null +++ b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/model/playback/PlaybackStatus.kt @@ -0,0 +1,5 @@ +package com.quran.labs.androidquran.common.audio.model.playback + +enum class PlaybackStatus { + PREPARING, PLAYING, PAUSED +} diff --git a/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/repository/AudioStatusRepository.kt b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/repository/AudioStatusRepository.kt new file mode 100644 index 0000000000..64e8444150 --- /dev/null +++ b/common/audio/src/main/java/com/quran/labs/androidquran/common/audio/repository/AudioStatusRepository.kt @@ -0,0 +1,19 @@ +package com.quran.labs.androidquran.common.audio.repository + +import com.quran.labs.androidquran.common.audio.model.playback.AudioStatus +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AudioStatusRepository @Inject constructor() { + private val audioPlaybackInternalFlow = MutableStateFlow(AudioStatus.Stopped) + + val audioPlaybackFlow = audioPlaybackInternalFlow.asStateFlow() + + fun updateAyahPlayback(audioStatus: AudioStatus) { + audioPlaybackInternalFlow.value = audioStatus + } +} + diff --git a/common/reading/src/main/java/com/quran/reading/common/AudioEventPresenter.kt b/common/reading/src/main/java/com/quran/reading/common/AudioEventPresenter.kt deleted file mode 100644 index c6e597046e..0000000000 --- a/common/reading/src/main/java/com/quran/reading/common/AudioEventPresenter.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.quran.reading.common - -import com.quran.data.model.SuraAyah -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AudioEventPresenter @Inject constructor() { - private val audioPlaybackAyahInternalFlow = MutableStateFlow(null) - - val audioPlaybackAyahFlow: StateFlow = audioPlaybackAyahInternalFlow.asStateFlow() - - fun onAyahPlayback(suraAyah: SuraAyah?) { - if (audioPlaybackAyahInternalFlow.value != suraAyah) { - audioPlaybackAyahInternalFlow.value = suraAyah - } - } - - fun currentPlaybackAyah(): SuraAyah? = audioPlaybackAyahFlow.value -}