From 4e0e54936b5552377df80fb7cab8e1fbe65386c0 Mon Sep 17 00:00:00 2001 From: MahmouMabrok Date: Thu, 7 Jan 2021 22:12:46 +0200 Subject: [PATCH 1/8] feat: add speedup & down to sound player UI --- .../androidquran/service/AudioService.java | 90 ++++-- .../labs/androidquran/ui/PagerActivity.java | 15 + .../androidquran/view/AudioStatusBar.java | 303 ++++++++++-------- app/src/main/res/drawable/ic_neg_1_24.xml | 10 + app/src/main/res/drawable/ic_plus_1_24.xml | 10 + 5 files changed, 268 insertions(+), 160 deletions(-) create mode 100644 app/src/main/res/drawable/ic_neg_1_24.xml create mode 100644 app/src/main/res/drawable/ic_plus_1_24.xml diff --git a/app/src/main/java/com/quran/labs/androidquran/service/AudioService.java b/app/src/main/java/com/quran/labs/androidquran/service/AudioService.java index ac39521bb9..b5f1f4c694 100644 --- a/app/src/main/java/com/quran/labs/androidquran/service/AudioService.java +++ b/app/src/main/java/com/quran/labs/androidquran/service/AudioService.java @@ -1,8 +1,8 @@ -/* +/* * This code is based on the RandomMusicPlayer example from * the Android Open Source Project samples. It has been modified * for use in Quran Android. - * + * * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; +import android.media.PlaybackParams; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.AsyncTask; @@ -55,19 +56,21 @@ import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.SparseIntArray; + import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.media.app.NotificationCompat.MediaStyle; 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.dao.audio.AudioRequest; import com.quran.labs.androidquran.data.Constants; import com.quran.labs.androidquran.data.QuranDisplayData; -import com.quran.data.model.SuraAyah; import com.quran.labs.androidquran.database.DatabaseUtils; import com.quran.labs.androidquran.database.SuraTimingDatabaseHandler; import com.quran.labs.androidquran.extension.SuraAyahExtensionKt; @@ -78,12 +81,15 @@ import com.quran.labs.androidquran.ui.PagerActivity; import com.quran.labs.androidquran.util.AudioUtils; import com.quran.labs.androidquran.util.NotificationChannelUtil; -import io.reactivex.Maybe; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; + import java.io.File; import java.io.IOException; + import javax.inject.Inject; + +import io.reactivex.Maybe; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; import timber.log.Timber; /** @@ -104,6 +110,8 @@ public class AudioService extends Service implements OnCompletionListener, public static final String ACTION_PAUSE = "com.quran.labs.androidquran.action.PAUSE"; public static final String ACTION_STOP = "com.quran.labs.androidquran.action.STOP"; public static final String ACTION_SKIP = "com.quran.labs.androidquran.action.SKIP"; + public static final String ACTION_INCREASE_SPEAD = "com.quran.labs.androidquran.action.INCREASE_SPEAD"; + public static final String ACTION_DECREASE_SPEAD = "com.quran.labs.androidquran.action.DECREASE_SPEAD"; public static final String ACTION_REWIND = "com.quran.labs.androidquran.action.REWIND"; public static final String ACTION_CONNECT = "com.quran.labs.androidquran.action.CONNECT"; public static final String ACTION_UPDATE_REPEAT = "com.quran.labs.androidquran.action.UPDATE_REPEAT"; @@ -210,9 +218,12 @@ private enum AudioFocus { private AsyncTask timingTask = null; private final CompositeDisposable compositeDisposable = new CompositeDisposable(); - @Inject QuranInfo quranInfo; - @Inject QuranDisplayData quranDisplayData; - @Inject AudioUtils audioUtils; + @Inject + QuranInfo quranInfo; + @Inject + QuranDisplayData quranDisplayData; + @Inject + AudioUtils audioUtils; private static final int MSG_INCOMING = 1; private static final int MSG_START_AUDIO = 2; @@ -319,8 +330,8 @@ public void onCreate() { compositeDisposable.add( Maybe.fromCallable(this::generateNotificationIcon) - .subscribeOn(Schedulers.io()) - .subscribe(bitmap -> notificationIcon = bitmap)); + .subscribeOn(Schedulers.io()) + .subscribe(bitmap -> notificationIcon = bitmap)); } private class MediaSessionCallback extends MediaSessionCompat.Callback { @@ -432,6 +443,10 @@ private void handleIntent(Intent intent) { processStopRequest(); } else if (ACTION_REWIND.equals(action)) { processRewindRequest(); + } else if (ACTION_INCREASE_SPEAD.equals(action)) { + processIncreasePlayback(); + } else if (ACTION_DECREASE_SPEAD.equals(action)) { + processDecreasePlayback(); } else if (ACTION_UPDATE_REPEAT.equals(action)) { final AudioRequest playInfo = intent.getParcelableExtra(EXTRA_PLAY_INFO); if (playInfo != null && audioQueue != null) { @@ -736,6 +751,19 @@ private void processRewindRequest() { } } + private void processIncreasePlayback() { + if (State.Playing == state) { + increasePlayback(); + } + } + + private void processDecreasePlayback() { + if (State.Playing == state) { + decreasePlayback(); + } + } + + private void processSkipRequest() { if (audioRequest == null) { return; @@ -928,6 +956,22 @@ private void configAndStartMediaPlayer(boolean canSeek) { } } + private void increasePlayback() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PlaybackParams params = player.getPlaybackParams(); + params.setSpeed(params.getSpeed() + 0.15f); + player.setPlaybackParams(params); + } + } + + private void decreasePlayback() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PlaybackParams params = player.getPlaybackParams(); + params.setSpeed(params.getSpeed() - 0.15f); + player.setPlaybackParams(params); + } + } + private void tryToGetAudioFocus() { if (audioFocus != AudioFocus.Focused && audioFocusHelper != null && audioFocusHelper.requestFocus()) { @@ -1065,12 +1109,12 @@ private void setState(int state) { builder.setState(state, position, 1.0f); builder.setActions( PlaybackStateCompat.ACTION_PLAY | - PlaybackStateCompat.ACTION_STOP | - PlaybackStateCompat.ACTION_REWIND | - PlaybackStateCompat.ACTION_FAST_FORWARD | - PlaybackStateCompat.ACTION_PAUSE | - PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | - PlaybackStateCompat.ACTION_SKIP_TO_NEXT); + PlaybackStateCompat.ACTION_STOP | + PlaybackStateCompat.ACTION_REWIND | + PlaybackStateCompat.ACTION_FAST_FORWARD | + PlaybackStateCompat.ACTION_PAUSE | + PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | + PlaybackStateCompat.ACTION_SKIP_TO_NEXT); mediaSession.setPlaybackState(builder.build()); } @@ -1083,7 +1127,9 @@ public void onSeekComplete(MediaPlayer mediaPlayer) { serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 200); } - /** Called when media player is done playing current file. */ + /** + * Called when media player is done playing current file. + */ @Override public void onCompletion(MediaPlayer player) { // The media player finished playing the current file, so @@ -1112,7 +1158,9 @@ public void onCompletion(MediaPlayer player) { } } - /** Called when media player is done preparing. */ + /** + * Called when media player is done preparing. + */ @Override public void onPrepared(MediaPlayer player) { Timber.d("okay, prepared!"); @@ -1145,7 +1193,9 @@ public void onPrepared(MediaPlayer player) { configAndStartMediaPlayer(); } - /** Updates the notification. */ + /** + * Updates the notification. + */ void updateNotification() { notificationBuilder.setContentText(getTitle()); if (!didSetNotificationIconOnNotificationBuilder && notificationIcon != null) { 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 cac29b10b6..290e067a49 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 @@ -1574,6 +1574,21 @@ public void onPausePressed() { audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); } + @Override + public void onUpPressed() { + startService(audioUtils.getAudioIntent( + this, AudioService.ACTION_INCREASE_SPEAD)); + // audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); + } + + @Override + public void onDownPressed() { + startService(audioUtils.getAudioIntent( + this, AudioService.ACTION_DECREASE_SPEAD)); + // audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); + } + + @Override public void onNextPressed() { startService(audioUtils.getAudioIntent(this, 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 c761848770..56d9093299 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 @@ -20,6 +20,12 @@ import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.DrawableRes; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.core.view.ViewCompat; + import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.common.audio.QariItem; import com.quran.labs.androidquran.data.Constants; @@ -28,12 +34,6 @@ import java.util.List; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.core.view.ViewCompat; - public class AudioStatusBar extends LeftToRightLinearLayout { public static final int STOPPED_MODE = 1; @@ -43,44 +43,85 @@ public class AudioStatusBar extends LeftToRightLinearLayout { public static final int PAUSED_MODE = 5; public static final int PROMPT_DOWNLOAD_MODE = 6; - private Context context; + private final Context context; private int currentMode; - private int buttonWidth; - private int separatorWidth; - private int separatorSpacing; - private int textFontSize; - private int textFullFontSize; - private int spinnerPadding; + private final int buttonWidth; + private final int separatorWidth; + private final int separatorSpacing; + private final int textFontSize; + private final int textFullFontSize; + private final int spinnerPadding; private QariAdapter adapter; private int currentQari; private int currentRepeat = 0; - @DrawableRes private int itemBackground; - private boolean isRtl; + private final boolean isRtl; + private final SharedPreferences sharedPreferences; private boolean isDualPageMode; private boolean hasErrorText; private boolean haveCriticalError = false; - private SharedPreferences sharedPreferences; + private final int[] repeatValues = {0, 1, 2, 3, -1}; private QuranSpinner spinner; private TextView progressText; private ProgressBar progressBar; private RepeatButton repeatButton; private AudioBarListener audioBarListener; + @DrawableRes + private int itemBackground; + OnClickListener onClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + if (audioBarListener != null) { + int tag = (Integer) view.getTag(); + switch (tag) { + case R.drawable.ic_play: + audioBarListener.onPlayPressed(); + break; + case R.drawable.ic_stop: + audioBarListener.onStopPressed(); + break; - private int[] repeatValues = {0, 1, 2, 3, -1}; + case R.drawable.ic_plus_1_24: + audioBarListener.onUpPressed(); + break; + + case R.drawable.ic_neg_1_24: + audioBarListener.onDownPressed(); + break; - public interface AudioBarListener { - void onPlayPressed(); - void onPausePressed(); - void onNextPressed(); - void onPreviousPressed(); - void onStopPressed(); - void onCancelPressed(boolean stopDownload); - void setRepeatCount(int repeatCount); - void onAcceptPressed(); - void onAudioSettingsPressed(); - } + + case R.drawable.ic_pause: + audioBarListener.onPausePressed(); + break; + case R.drawable.ic_next: + audioBarListener.onNextPressed(); + break; + case R.drawable.ic_previous: + audioBarListener.onPreviousPressed(); + break; + case R.drawable.ic_repeat: + incrementRepeat(); + audioBarListener.setRepeatCount(repeatValues[currentRepeat]); + break; + case R.drawable.ic_cancel: + if (haveCriticalError) { + haveCriticalError = false; + switchMode(STOPPED_MODE); + } else { + audioBarListener.onCancelPressed(currentMode == DOWNLOADING_MODE); + } + break; + case R.drawable.ic_accept: + audioBarListener.onAcceptPressed(); + break; + case R.drawable.ic_action_settings: + audioBarListener.onAudioSettingsPressed(); + break; + } + } + } + }; public AudioStatusBar(Context context) { this(context, null); @@ -164,11 +205,7 @@ public void switchMode(int mode) { showPromptForDownloadMode(); } else if (mode == DOWNLOADING_MODE || mode == LOADING_MODE) { showProgress(mode); - } else if (mode == PLAYING_MODE) { - showPlayingMode(false); - } else { - showPlayingMode(true); - } + } else showPlayingMode(mode != PLAYING_MODE); } @NonNull @@ -228,60 +265,32 @@ private void showStoppedMode() { } } - private static class QariAdapter extends BaseAdapter { - @NonNull LayoutInflater inflater; - @NonNull private final List items; - @LayoutRes private final int layoutViewId; - @LayoutRes private final int dropDownViewId; - - QariAdapter(@NonNull Context context, - @NonNull List items, - @LayoutRes int layoutViewId, - @LayoutRes int dropDownViewId) { - this.items = items; - this.layoutViewId = layoutViewId; - this.dropDownViewId = dropDownViewId; - inflater = LayoutInflater.from(context); - } - - @Override - public int getCount() { - return items.size(); - } - - @Override - public QariItem getItem(int position) { - return items.get(position); - } + private void showPlayingMode(boolean isPaused) { + removeAllViews(); - @Override - public long getItemId(int position) { - return position; - } + final boolean withWeight = !isDualPageMode; - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return getViewInternal(position, convertView, parent, layoutViewId); + int button; + if (isPaused) { + button = R.drawable.ic_play; + currentMode = PAUSED_MODE; + } else { + button = R.drawable.ic_pause; + currentMode = PLAYING_MODE; } - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - return getViewInternal(position, convertView, parent, dropDownViewId); - } + addButton(R.drawable.ic_stop, withWeight); + addButton(R.drawable.ic_previous, withWeight); + addButton(button, withWeight); + addButton(R.drawable.ic_next, withWeight); + addButton(R.drawable.ic_plus_1_24, withWeight); + addButton(R.drawable.ic_neg_1_24, withWeight); - private View getViewInternal(int position, View convertView, - ViewGroup parent, @LayoutRes int resource) { - TextView textView; - if (convertView == null) { - textView = (TextView) inflater.inflate(resource, parent, false); - } else { - textView = (TextView) convertView; - } + repeatButton = new RepeatButton(context); + addButton(repeatButton, R.drawable.ic_repeat, withWeight); + updateRepeatButtonText(); - QariItem item = getItem(position); - textView.setText(item.getName()); - return textView; - } + addButton(R.drawable.ic_action_settings, withWeight); } private void addSpinner() { @@ -399,30 +408,28 @@ private void addDownloadProgress(@StringRes int text) { addView(ll, lp); } - private void showPlayingMode(boolean isPaused) { - removeAllViews(); + public interface AudioBarListener { + void onPlayPressed(); - final boolean withWeight = !isDualPageMode; + void onPausePressed(); - int button; - if (isPaused) { - button = R.drawable.ic_play; - currentMode = PAUSED_MODE; - } else { - button = R.drawable.ic_pause; - currentMode = PLAYING_MODE; - } + void onNextPressed(); - addButton(R.drawable.ic_stop, withWeight); - addButton(R.drawable.ic_previous, withWeight); - addButton(button, withWeight); - addButton(R.drawable.ic_next, withWeight); + void onUpPressed(); - repeatButton = new RepeatButton(context); - addButton(repeatButton, R.drawable.ic_repeat, withWeight); - updateRepeatButtonText(); + void onDownPressed(); - addButton(R.drawable.ic_action_settings, withWeight); + void onPreviousPressed(); + + void onStopPressed(); + + void onCancelPressed(boolean stopDownload); + + void setRepeatCount(int repeatCount); + + void onAcceptPressed(); + + void onAudioSettingsPressed(); } private void addButton(int imageId, boolean withWeight) { @@ -498,47 +505,63 @@ public void setAudioBarListener(AudioBarListener listener) { audioBarListener = listener; } - OnClickListener onClickListener = new OnClickListener() { + private static class QariAdapter extends BaseAdapter { + @NonNull + private final List items; + @LayoutRes + private final int layoutViewId; + @LayoutRes + private final int dropDownViewId; + @NonNull + LayoutInflater inflater; + + QariAdapter(@NonNull Context context, + @NonNull List items, + @LayoutRes int layoutViewId, + @LayoutRes int dropDownViewId) { + this.items = items; + this.layoutViewId = layoutViewId; + this.dropDownViewId = dropDownViewId; + inflater = LayoutInflater.from(context); + } + @Override - public void onClick(View view) { - if (audioBarListener != null) { - int tag = (Integer) view.getTag(); - switch (tag) { - case R.drawable.ic_play: - audioBarListener.onPlayPressed(); - break; - case R.drawable.ic_stop: - audioBarListener.onStopPressed(); - break; - case R.drawable.ic_pause: - audioBarListener.onPausePressed(); - break; - case R.drawable.ic_next: - audioBarListener.onNextPressed(); - break; - case R.drawable.ic_previous: - audioBarListener.onPreviousPressed(); - break; - case R.drawable.ic_repeat: - incrementRepeat(); - audioBarListener.setRepeatCount(repeatValues[currentRepeat]); - break; - case R.drawable.ic_cancel: - if (haveCriticalError) { - haveCriticalError = false; - switchMode(STOPPED_MODE); - } else { - audioBarListener.onCancelPressed(currentMode == DOWNLOADING_MODE); - } - break; - case R.drawable.ic_accept: - audioBarListener.onAcceptPressed(); - break; - case R.drawable.ic_action_settings: - audioBarListener.onAudioSettingsPressed(); - break; - } + public int getCount() { + return items.size(); + } + + @Override + public QariItem getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getViewInternal(position, convertView, parent, layoutViewId); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return getViewInternal(position, convertView, parent, dropDownViewId); + } + + private View getViewInternal(int position, View convertView, + ViewGroup parent, @LayoutRes int resource) { + TextView textView; + if (convertView == null) { + textView = (TextView) inflater.inflate(resource, parent, false); + } else { + textView = (TextView) convertView; } + + QariItem item = getItem(position); + textView.setText(item.getName()); + return textView; } - }; + } } diff --git a/app/src/main/res/drawable/ic_neg_1_24.xml b/app/src/main/res/drawable/ic_neg_1_24.xml new file mode 100644 index 0000000000..c9830ca293 --- /dev/null +++ b/app/src/main/res/drawable/ic_neg_1_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_plus_1_24.xml b/app/src/main/res/drawable/ic_plus_1_24.xml new file mode 100644 index 0000000000..0e2de79573 --- /dev/null +++ b/app/src/main/res/drawable/ic_plus_1_24.xml @@ -0,0 +1,10 @@ + + + From 22a270cdb5d2526c59164c81d19b787ac06d4cd8 Mon Sep 17 00:00:00 2001 From: MahmoudMabrok Date: Sun, 24 Dec 2023 14:37:11 +0200 Subject: [PATCH 2/8] feat: handle speedup and down --- .../labs/androidquran/service/AudioService.kt | 45 ++++++++++++++++++- .../labs/androidquran/ui/PagerActivity.java | 11 ++--- .../androidquran/view/AudioStatusBar.java | 8 ++++ 3 files changed, 56 insertions(+), 8 deletions(-) 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 df7e308080..1efc28da53 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 @@ -398,7 +398,12 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, processStopRequest() } else if (ACTION_REWIND == action) { processRewindRequest() - } else if (ACTION_UPDATE_REPEAT == action) { + }else if (ACTION_SPEED_DOWN == action){ + processSpeedDownPlayback() + }else if (ACTION_SPEED_UP == action){ + processSpeedUpPlayback() + } + else if (ACTION_UPDATE_REPEAT == action) { val playInfo = intent.getParcelableExtra(EXTRA_PLAY_INFO) val localAudioQueue = audioQueue if (playInfo != null && localAudioQueue != null) { @@ -677,7 +682,43 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } } } + private fun processSpeedUpPlayback() { + if (State.Playing === state) { + speedUpPlayback() + } + } + + private fun processSpeedDownPlayback() { + if (State.Playing === state) { + speedDownPlayback() + } + } + private fun speedUpPlayback() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + player?.playbackParams?.let { params -> + val newSpeed = params.speed + 0.15f + // todo should be handled based on Qari type + if (newSpeed <= 1.5){ + params.setSpeed(newSpeed) + player?.playbackParams = params + } + } + } + } + + private fun speedDownPlayback() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + player?.playbackParams?.let { params -> + val newSpeed = params.speed - 0.1f + // todo should be handled based on Qari type + if (newSpeed >= 0.5){ + params.setSpeed(newSpeed) + player?.playbackParams = params + } + } + } + } private fun processSkipRequest() { if (audioRequest == null) { return @@ -1361,6 +1402,8 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, const val ACTION_STOP = "com.quran.labs.androidquran.action.STOP" const val ACTION_SKIP = "com.quran.labs.androidquran.action.SKIP" const val ACTION_REWIND = "com.quran.labs.androidquran.action.REWIND" + const val ACTION_SPEED_UP = "com.quran.labs.androidquran.action.SPEED_UP" + const val ACTION_SPEED_DOWN = "com.quran.labs.androidquran.action.SPEED_DOWN" const val ACTION_CONNECT = "com.quran.labs.androidquran.action.CONNECT" const val ACTION_UPDATE_REPEAT = "com.quran.labs.androidquran.action.UPDATE_REPEAT" 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 6ea76c3102..977a828399 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 @@ -1585,19 +1585,16 @@ public void onPausePressed() { @Override public void onUpPressed() { - startService(audioUtils.getAudioIntent( - this, AudioService.ACTION_INCREASE_SPEAD)); - // audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); + startService(audioUtils.getAudioIntent(this, + AudioService.ACTION_SPEED_UP)); } @Override public void onDownPressed() { - startService(audioUtils.getAudioIntent( - this, AudioService.ACTION_DECREASE_SPEAD)); - // audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); + startService(audioUtils.getAudioIntent(this, + AudioService.ACTION_SPEED_DOWN)); } - @Override public void onNextPressed() { startService(audioUtils.getAudioIntent(this, 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 3e74737d25..63a79dbbbe 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 @@ -74,6 +74,8 @@ public interface AudioBarListener { void onNextPressed(); void onPreviousPressed(); void onStopPressed(); + void onDownPressed(); + void onUpPressed(); void onCancelPressed(boolean stopDownload); void setRepeatCount(int repeatCount); void onAcceptPressed(); @@ -460,6 +462,8 @@ private void showPlayingMode(boolean isPaused) { addButton(R.drawable.ic_previous, withWeight); addButton(button, withWeight); addButton(R.drawable.ic_next, withWeight); + addButton(R.drawable.ic_neg_1_24, withWeight); + addButton(R.drawable.ic_plus_1_24, withWeight); addButton(repeatButton, R.drawable.ic_repeat, withWeight); updateRepeatButtonText(); @@ -571,6 +575,10 @@ public void onClick(View view) { } } else if (tag == R.drawable.ic_next) { audioBarListener.onNextPressed(); + } else if (tag == R.drawable.ic_neg_1_24) { + audioBarListener.onDownPressed(); + } else if (tag == R.drawable.ic_plus_1_24) { + audioBarListener.onUpPressed(); } else if (tag == R.drawable.ic_previous) { audioBarListener.onPreviousPressed(); } else if (tag == R.drawable.ic_repeat) { From f0f984718ad78849aa92ae116ad6f7ff7516239e Mon Sep 17 00:00:00 2001 From: MahmoudMabrok Date: Sun, 24 Dec 2023 21:58:40 +0200 Subject: [PATCH 3/8] feat: handle speedup as pre-set --- .../labs/androidquran/service/AudioService.kt | 49 ++++--------------- .../labs/androidquran/ui/PagerActivity.java | 11 ++--- .../androidquran/view/AudioStatusBar.java | 47 +++++++++++++++--- .../labs/androidquran/view/RepeatButton.kt | 4 +- app/src/main/res/drawable/ic_neg_1_24.xml | 10 ---- app/src/main/res/drawable/ic_plus_1_24.xml | 10 ---- app/src/main/res/drawable/ic_speed.xml | 5 ++ 7 files changed, 60 insertions(+), 76 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_neg_1_24.xml delete mode 100644 app/src/main/res/drawable/ic_plus_1_24.xml create mode 100644 app/src/main/res/drawable/ic_speed.xml 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 1efc28da53..c95f41bc79 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 @@ -398,10 +398,9 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, processStopRequest() } else if (ACTION_REWIND == action) { processRewindRequest() - }else if (ACTION_SPEED_DOWN == action){ - processSpeedDownPlayback() - }else if (ACTION_SPEED_UP == action){ - processSpeedUpPlayback() + }else if (ACTION_SPEED_UPDATE == action){ + val speed = intent.getFloatExtra(EXTRA_PLAY_SPEED, 1f) + processUpdatePlaybackSpeed(speed) } else if (ACTION_UPDATE_REPEAT == action) { val playInfo = intent.getParcelableExtra(EXTRA_PLAY_INFO) @@ -682,43 +681,15 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } } } - private fun processSpeedUpPlayback() { - if (State.Playing === state) { - speedUpPlayback() - } - } - - private fun processSpeedDownPlayback() { - if (State.Playing === state) { - speedDownPlayback() - } - } - - private fun speedUpPlayback() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - player?.playbackParams?.let { params -> - val newSpeed = params.speed + 0.15f - // todo should be handled based on Qari type - if (newSpeed <= 1.5){ - params.setSpeed(newSpeed) - player?.playbackParams = params - } - } - } - } - - private fun speedDownPlayback() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + private fun processUpdatePlaybackSpeed(speed: Float) { + if (State.Playing === state && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { player?.playbackParams?.let { params -> - val newSpeed = params.speed - 0.1f - // todo should be handled based on Qari type - if (newSpeed >= 0.5){ - params.setSpeed(newSpeed) - player?.playbackParams = params - } + params.setSpeed(speed) + player?.playbackParams = params } } } + private fun processSkipRequest() { if (audioRequest == null) { return @@ -1402,8 +1373,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, const val ACTION_STOP = "com.quran.labs.androidquran.action.STOP" const val ACTION_SKIP = "com.quran.labs.androidquran.action.SKIP" const val ACTION_REWIND = "com.quran.labs.androidquran.action.REWIND" - const val ACTION_SPEED_UP = "com.quran.labs.androidquran.action.SPEED_UP" - const val ACTION_SPEED_DOWN = "com.quran.labs.androidquran.action.SPEED_DOWN" + const val ACTION_SPEED_UPDATE = "com.quran.labs.androidquran.action.SPEED_UPDATE" const val ACTION_CONNECT = "com.quran.labs.androidquran.action.CONNECT" const val ACTION_UPDATE_REPEAT = "com.quran.labs.androidquran.action.UPDATE_REPEAT" @@ -1421,6 +1391,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, // so user can pass in a serializable LegacyAudioRequest to the intent const val EXTRA_PLAY_INFO = "com.quran.labs.androidquran.PLAY_INFO" + const val EXTRA_PLAY_SPEED = "com.quran.labs.androidquran.PLAY_SPEED" private const val NOTIFICATION_CHANNEL_ID = Constants.AUDIO_CHANNEL private const val MSG_INCOMING = 1 private const val MSG_START_AUDIO = 2 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 977a828399..f6fcaeb622 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 @@ -1563,6 +1563,7 @@ public void proceedWithDownload(Intent downloadIntent) { public void handlePlayback(AudioRequest request) { needsPermissionToDownloadOver3g = true; + audioStatusBar.resetSpeed(); final Intent intent = new Intent(this, AudioService.class); intent.setAction(AudioService.ACTION_PLAYBACK); if (request != null) { @@ -1584,15 +1585,9 @@ public void onPausePressed() { } @Override - public void onUpPressed() { + public void setPlayBackSpeed(float speed) { startService(audioUtils.getAudioIntent(this, - AudioService.ACTION_SPEED_UP)); - } - - @Override - public void onDownPressed() { - startService(audioUtils.getAudioIntent(this, - AudioService.ACTION_SPEED_DOWN)); + AudioService.ACTION_SPEED_UPDATE).putExtra(AudioService.EXTRA_PLAY_SPEED, speed)); } @Override 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 63a79dbbbe..41d7d9d2f0 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 @@ -5,6 +5,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; +import android.os.Build; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -53,6 +54,10 @@ public class AudioStatusBar extends LeftToRightLinearLayout { private Qari currentQari; private int currentRepeat = 0; + private int defaultSpeedIndex = 2; + private int currentSpeedIndex = defaultSpeedIndex; + private final float[] speeds = { 0.5f, 0.75f, 1f, 1.25f, 1.5f}; + private float currentSpeed = speeds[currentSpeedIndex]; @DrawableRes private int itemBackground; private final boolean isRtl; private boolean isDualPageMode; @@ -65,17 +70,22 @@ public class AudioStatusBar extends LeftToRightLinearLayout { private TextView progressText; private ProgressBar progressBar; private final RepeatButton repeatButton; + private final RepeatButton speedButton; private AudioBarListener audioBarListener; private AudioBarRecitationListener audioBarRecitationListener; + public void resetSpeed() { + currentSpeedIndex = defaultSpeedIndex; + updateSpeedButtonText(); + } + public interface AudioBarListener { void onPlayPressed(); void onPausePressed(); void onNextPressed(); void onPreviousPressed(); void onStopPressed(); - void onDownPressed(); - void onUpPressed(); + void setPlayBackSpeed(float speed); void onCancelPressed(boolean stopDownload); void setRepeatCount(int repeatCount); void onAcceptPressed(); @@ -106,6 +116,7 @@ public AudioStatusBar(Context context, AttributeSet attrs, int defStyle) { this.context = context; repeatButton = new RepeatButton(context); + speedButton = new RepeatButton(context , false); Resources resources = getResources(); buttonWidth = resources.getDimensionPixelSize( R.dimen.audiobar_button_width); @@ -462,11 +473,13 @@ private void showPlayingMode(boolean isPaused) { addButton(R.drawable.ic_previous, withWeight); addButton(button, withWeight); addButton(R.drawable.ic_next, withWeight); - addButton(R.drawable.ic_neg_1_24, withWeight); - addButton(R.drawable.ic_plus_1_24, withWeight); addButton(repeatButton, R.drawable.ic_repeat, withWeight); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ + addButton(speedButton, R.drawable.ic_speed, withWeight); + } updateRepeatButtonText(); + updateSpeedButtonText(); addButton(R.drawable.ic_action_settings, withWeight); } @@ -515,6 +528,14 @@ private void incrementRepeat() { } updateRepeatButtonText(); } + private void updatePlayBackSpeed() { + currentSpeedIndex+=1; + currentSpeedIndex %= speeds.length; + currentSpeed = speeds[currentSpeedIndex]; + updateSpeedButtonText(); + } + + private void updateRepeatButtonText() { String str; @@ -528,6 +549,17 @@ private void updateRepeatButtonText() { repeatButton.setText(str); } + private void updateSpeedButtonText(){ + String str; + currentSpeed = speeds[currentSpeedIndex]; + if (currentSpeedIndex == 2) { + str = ""; + } else { + str = String.valueOf(currentSpeed); + } + speedButton.setText(str); + } + public void setRepeatCount(int repeatCount) { boolean updated = false; if (currentRepeat != repeatCount) { @@ -575,10 +607,9 @@ public void onClick(View view) { } } else if (tag == R.drawable.ic_next) { audioBarListener.onNextPressed(); - } else if (tag == R.drawable.ic_neg_1_24) { - audioBarListener.onDownPressed(); - } else if (tag == R.drawable.ic_plus_1_24) { - audioBarListener.onUpPressed(); + } else if (tag == R.drawable.ic_speed) { + updatePlayBackSpeed(); + audioBarListener.setPlayBackSpeed(currentSpeed); } else if (tag == R.drawable.ic_previous) { audioBarListener.onPreviousPressed(); } else if (tag == R.drawable.ic_repeat) { diff --git a/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt b/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt index 62371f694b..05306ba73e 100644 --- a/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt +++ b/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt @@ -11,6 +11,7 @@ import com.quran.labs.androidquran.R class RepeatButton @JvmOverloads constructor( context: Context, + private val shouldShiftText: Boolean = true, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : AppCompatImageView(context, attrs, defStyleAttr) { @@ -46,10 +47,11 @@ class RepeatButton @JvmOverloads constructor( private fun updateCoordinates() { canDraw = false val drawable = drawable + val xDivider = if (shouldShiftText) 2 else 1 if (drawable != null) { val bounds = drawable.bounds if (bounds.width() > 0) { - textXPosition = viewWidth - (viewWidth - bounds.width()) / 2 + textXPosition = viewWidth - (viewWidth - bounds.width()) / xDivider textYPosition = textYPadding + (viewHeight - bounds.height()) / 2 canDraw = true } diff --git a/app/src/main/res/drawable/ic_neg_1_24.xml b/app/src/main/res/drawable/ic_neg_1_24.xml deleted file mode 100644 index c9830ca293..0000000000 --- a/app/src/main/res/drawable/ic_neg_1_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_plus_1_24.xml b/app/src/main/res/drawable/ic_plus_1_24.xml deleted file mode 100644 index 0e2de79573..0000000000 --- a/app/src/main/res/drawable/ic_plus_1_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_speed.xml b/app/src/main/res/drawable/ic_speed.xml new file mode 100644 index 0000000000..003852477a --- /dev/null +++ b/app/src/main/res/drawable/ic_speed.xml @@ -0,0 +1,5 @@ + + + + + From 86792a09d26cbd90082019c1a36e5fb174da4a59 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Tue, 26 Dec 2023 01:24:02 +0400 Subject: [PATCH 4/8] Minor cleanup --- .../labs/androidquran/service/AudioService.kt | 5 ++-- .../androidquran/view/AudioStatusBar.java | 24 +++++++++---------- .../labs/androidquran/view/RepeatButton.kt | 9 ++++--- app/src/main/res/drawable/ic_speed.xml | 2 -- 4 files changed, 19 insertions(+), 21 deletions(-) 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 c95f41bc79..d6c56dae3a 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 @@ -398,11 +398,10 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, processStopRequest() } else if (ACTION_REWIND == action) { processRewindRequest() - }else if (ACTION_SPEED_UPDATE == action){ + } else if (ACTION_SPEED_UPDATE == action){ val speed = intent.getFloatExtra(EXTRA_PLAY_SPEED, 1f) processUpdatePlaybackSpeed(speed) - } - else if (ACTION_UPDATE_REPEAT == action) { + } else if (ACTION_UPDATE_REPEAT == action) { val playInfo = intent.getParcelableExtra(EXTRA_PLAY_INFO) val localAudioQueue = audioQueue if (playInfo != null && localAudioQueue != null) { 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 41d7d9d2f0..19a17c2889 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 @@ -74,11 +74,6 @@ public class AudioStatusBar extends LeftToRightLinearLayout { private AudioBarListener audioBarListener; private AudioBarRecitationListener audioBarRecitationListener; - public void resetSpeed() { - currentSpeedIndex = defaultSpeedIndex; - updateSpeedButtonText(); - } - public interface AudioBarListener { void onPlayPressed(); void onPausePressed(); @@ -116,7 +111,7 @@ public AudioStatusBar(Context context, AttributeSet attrs, int defStyle) { this.context = context; repeatButton = new RepeatButton(context); - speedButton = new RepeatButton(context , false); + speedButton = new RepeatButton(context); Resources resources = getResources(); buttonWidth = resources.getDimensionPixelSize( R.dimen.audiobar_button_width); @@ -201,6 +196,11 @@ public QariItem getAudioInfo() { return QariItem.Companion.fromQari(context, currentQari); } + public void resetSpeed() { + currentSpeedIndex = defaultSpeedIndex; + updateSpeedButtonText(); + } + public void setProgress(int progress) { if (hasErrorText) { progressText.setText(R.string.downloading_title); @@ -528,17 +528,15 @@ private void incrementRepeat() { } updateRepeatButtonText(); } + private void updatePlayBackSpeed() { - currentSpeedIndex+=1; - currentSpeedIndex %= speeds.length; + currentSpeedIndex = (currentSpeedIndex + 1) % speeds.length; currentSpeed = speeds[currentSpeedIndex]; updateSpeedButtonText(); - } - - + } private void updateRepeatButtonText() { - String str; + final String str; if (currentRepeat == -1) { str = context.getString(R.string.infinity); } else if (currentRepeat == 0) { @@ -550,8 +548,8 @@ private void updateRepeatButtonText() { } private void updateSpeedButtonText(){ - String str; currentSpeed = speeds[currentSpeedIndex]; + final String str; if (currentSpeedIndex == 2) { str = ""; } else { diff --git a/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt b/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt index 05306ba73e..fbeaae39ea 100644 --- a/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt +++ b/app/src/main/java/com/quran/labs/androidquran/view/RepeatButton.kt @@ -11,7 +11,6 @@ import com.quran.labs.androidquran.R class RepeatButton @JvmOverloads constructor( context: Context, - private val shouldShiftText: Boolean = true, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : AppCompatImageView(context, attrs, defStyleAttr) { @@ -47,11 +46,15 @@ class RepeatButton @JvmOverloads constructor( private fun updateCoordinates() { canDraw = false val drawable = drawable - val xDivider = if (shouldShiftText) 2 else 1 if (drawable != null) { val bounds = drawable.bounds if (bounds.width() > 0) { - textXPosition = viewWidth - (viewWidth - bounds.width()) / xDivider + val x = viewWidth - (viewWidth - bounds.width()) / 2 + textXPosition = if (x + bounds.width() > viewWidth) { + viewWidth - bounds.width() + } else { + x + } textYPosition = textYPadding + (viewHeight - bounds.height()) / 2 canDraw = true } diff --git a/app/src/main/res/drawable/ic_speed.xml b/app/src/main/res/drawable/ic_speed.xml index 003852477a..807b50d0ec 100644 --- a/app/src/main/res/drawable/ic_speed.xml +++ b/app/src/main/res/drawable/ic_speed.xml @@ -1,5 +1,3 @@ - - From 331749f463f877fa5a6d6f785dbf798debd106a7 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sat, 6 Jan 2024 12:33:37 +0400 Subject: [PATCH 5/8] Persist audio playback speed in the audio request Also add the wiring to allow for exposing a speed setting in the audio playback panel. --- .../androidquran/dao/audio/AudioRequest.kt | 3 +- .../presenter/audio/AudioPresenter.kt | 3 +- .../labs/androidquran/service/AudioService.kt | 20 +++++------ .../labs/androidquran/ui/PagerActivity.java | 35 ++++++++++++++----- .../ui/fragment/AyahPlaybackFragment.kt | 5 +-- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/audio/AudioRequest.kt b/app/src/main/java/com/quran/labs/androidquran/dao/audio/AudioRequest.kt index d24f389fff..fc52f9c542 100644 --- a/app/src/main/java/com/quran/labs/androidquran/dao/audio/AudioRequest.kt +++ b/app/src/main/java/com/quran/labs/androidquran/dao/audio/AudioRequest.kt @@ -1,8 +1,8 @@ package com.quran.labs.androidquran.dao.audio import android.os.Parcelable -import com.quran.labs.androidquran.common.audio.model.QariItem import com.quran.data.model.SuraAyah +import com.quran.labs.androidquran.common.audio.model.QariItem import kotlinx.parcelize.Parcelize @Parcelize @@ -12,6 +12,7 @@ data class AudioRequest(val start: SuraAyah, val repeatInfo: Int = 0, val rangeRepeatInfo: Int = 0, val enforceBounds: Boolean, + val playbackSpeed: Float = 1f, val shouldStream: Boolean, val audioPathInfo: AudioPathInfo) : Parcelable { fun isGapless() = qari.isGapless diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/audio/AudioPresenter.kt b/app/src/main/java/com/quran/labs/androidquran/presenter/audio/AudioPresenter.kt index 0be9b85176..4eff9736af 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/audio/AudioPresenter.kt +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/audio/AudioPresenter.kt @@ -32,6 +32,7 @@ constructor(private val quranDisplayData: QuranDisplayData, verseRepeat: Int, rangeRepeat: Int, enforceRange: Boolean, + playbackSpeed: Float, shouldStream: Boolean) { val audioPathInfo = getLocalAudioPathInfo(qari) if (audioPathInfo != null) { @@ -62,7 +63,7 @@ constructor(private val quranDisplayData: QuranDisplayData, } val audioRequest = AudioRequest( - actualStart, actualEnd, qari, verseRepeat, rangeRepeat, enforceRange, stream, audioPath) + actualStart, actualEnd, qari, verseRepeat, rangeRepeat, enforceRange, playbackSpeed, stream, audioPath) play(audioRequest) } } 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 d6c56dae3a..da40936e73 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 @@ -398,14 +398,14 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, processStopRequest() } else if (ACTION_REWIND == action) { processRewindRequest() - } else if (ACTION_SPEED_UPDATE == action){ - val speed = intent.getFloatExtra(EXTRA_PLAY_SPEED, 1f) - processUpdatePlaybackSpeed(speed) - } else if (ACTION_UPDATE_REPEAT == action) { + } else if (ACTION_UPDATE_SETTINGS == action) { val playInfo = intent.getParcelableExtra(EXTRA_PLAY_INFO) val localAudioQueue = audioQueue if (playInfo != null && localAudioQueue != null) { audioQueue = localAudioQueue.withUpdatedAudioRequest(playInfo) + if (playInfo.playbackSpeed != audioRequest?.playbackSpeed) { + processUpdatePlaybackSpeed(playInfo.playbackSpeed) + } audioRequest = playInfo } } else { @@ -680,6 +680,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } } } + private fun processUpdatePlaybackSpeed(speed: Float) { if (State.Playing === state && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { player?.playbackParams?.let { params -> @@ -1059,6 +1060,9 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, ) player.start() state = State.Playing + audioRequest?.playbackSpeed?.let { speed -> + processUpdatePlaybackSpeed(speed) + } serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 200) } @@ -1362,19 +1366,15 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } companion object { - // These are the Intent actions that we are prepared to handle. Notice that - // the fact these constants exist in our class is a mere convenience: what - // really defines the actions our service can handle are the tags - // in the tag for our service in AndroidManifest.xml. + // These are the Intent actions that we are prepared to handle. const val ACTION_PLAYBACK = "com.quran.labs.androidquran.action.PLAYBACK" const val ACTION_PLAY = "com.quran.labs.androidquran.action.PLAY" const val ACTION_PAUSE = "com.quran.labs.androidquran.action.PAUSE" const val ACTION_STOP = "com.quran.labs.androidquran.action.STOP" const val ACTION_SKIP = "com.quran.labs.androidquran.action.SKIP" const val ACTION_REWIND = "com.quran.labs.androidquran.action.REWIND" - const val ACTION_SPEED_UPDATE = "com.quran.labs.androidquran.action.SPEED_UPDATE" const val ACTION_CONNECT = "com.quran.labs.androidquran.action.CONNECT" - const val ACTION_UPDATE_REPEAT = "com.quran.labs.androidquran.action.UPDATE_REPEAT" + const val ACTION_UPDATE_SETTINGS = "com.quran.labs.androidquran.action.UPDATE_SETTINGS" // pending notification request codes private const val REQUEST_CODE_MAIN = 0 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 f6fcaeb622..430da9f023 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 @@ -1496,7 +1496,7 @@ private void playFromAyah(int startSura, int startAyah) { final SuraAyah end = getSelectionEnd(); // handle the case of multiple ayat being selected and play them as a range if so final SuraAyah ending = (end == null || start.equals(end) || start.after(end))? null : end; - playFromAyah(start, ending, page, 0, 0, ending != null); + playFromAyah(start, ending, page, 0, 0, ending != null, 1.0f); } public void playFromAyah(SuraAyah start, @@ -1504,7 +1504,8 @@ public void playFromAyah(SuraAyah start, int page, int verseRepeat, int rangeRepeat, - boolean enforceRange) { + boolean enforceRange, + float playbackSpeed) { final SuraAyah ending = end != null ? end : audioUtils.getLastAyahToPlay(start, page, quranSettings.getPreferredDownloadAmount(), isDualPageVisible()); @@ -1516,7 +1517,7 @@ public void playFromAyah(SuraAyah start, final QariItem item = audioStatusBar.getAudioInfo(); final boolean shouldStream = quranSettings.shouldStream(); audioPresenter.play( - start, ending, item, verseRepeat, rangeRepeat, enforceRange, shouldStream); + start, ending, item, verseRepeat, rangeRepeat, enforceRange, playbackSpeed, shouldStream); } } @@ -1586,8 +1587,23 @@ public void onPausePressed() { @Override public void setPlayBackSpeed(float speed) { - startService(audioUtils.getAudioIntent(this, - AudioService.ACTION_SPEED_UPDATE).putExtra(AudioService.EXTRA_PLAY_SPEED, speed)); + if (lastAudioRequest != null) { + final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), + lastAudioRequest.getEnd(), + lastAudioRequest.getQari(), + lastAudioRequest.getRepeatInfo(), + lastAudioRequest.getRangeRepeatInfo(), + lastAudioRequest.getEnforceBounds(), + speed, + lastAudioRequest.getShouldStream(), + lastAudioRequest.getAudioPathInfo()); + + Intent i = new Intent(this, AudioService.class); + i.setAction(AudioService.ACTION_UPDATE_SETTINGS); + i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); + startService(i); + lastAudioRequest = updatedAudioRequest; + } } @Override @@ -1628,7 +1644,8 @@ public void onShowQariList() { } public boolean updatePlayOptions(int rangeRepeat, - int verseRepeat, boolean enforceRange) { + int verseRepeat, + boolean enforceRange) { if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1636,10 +1653,11 @@ public boolean updatePlayOptions(int rangeRepeat, verseRepeat, rangeRepeat, enforceRange, + lastAudioRequest.getPlaybackSpeed(), lastAudioRequest.getShouldStream(), lastAudioRequest.getAudioPathInfo()); Intent i = new Intent(this, AudioService.class); - i.setAction(AudioService.ACTION_UPDATE_REPEAT); + i.setAction(AudioService.ACTION_UPDATE_SETTINGS); i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); startService(i); @@ -1660,11 +1678,12 @@ public void setRepeatCount(int repeatCount) { repeatCount, lastAudioRequest.getRangeRepeatInfo(), lastAudioRequest.getEnforceBounds(), + lastAudioRequest.getPlaybackSpeed(), lastAudioRequest.getShouldStream(), lastAudioRequest.getAudioPathInfo()); Intent i = new Intent(this, AudioService.class); - i.setAction(AudioService.ACTION_UPDATE_REPEAT); + i.setAction(AudioService.ACTION_UPDATE_SETTINGS); i.putExtra(AudioService.EXTRA_PLAY_INFO, updatedAudioRequest); startService(i); lastAudioRequest = updatedAudioRequest; diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt index 14f64d024f..db90ed4208 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt +++ b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt @@ -189,12 +189,13 @@ class AyahPlaybackFragment : AyahActionFragment() { val enforceRange = restrictToRange.isChecked var updatedRange = false + val speed = 1.0f // TODO: expose a setting within this fragment also if (currentStart != decidedStart || currentEnding != decidedEnd) { // different range or not playing, so make a new request updatedRange = true context.playFromAyah( currentStart, currentEnding, page, verseRepeat, - rangeRepeat, enforceRange + rangeRepeat, enforceRange, speed ) } else if (shouldEnforce != enforceRange || rangeRepeatCount != rangeRepeat || verseRepeatCount != verseRepeat) { // can just update repeat settings @@ -202,7 +203,7 @@ class AyahPlaybackFragment : AyahActionFragment() { ) { // audio stopped in the process, let's start it context.playFromAyah( - currentStart, currentEnding, page, verseRepeat, rangeRepeat, enforceRange + currentStart, currentEnding, page, verseRepeat, rangeRepeat, enforceRange, speed ) } } From b57631b0667f5b789f6ad95b3db128cf46b266f1 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sat, 6 Jan 2024 14:21:08 +0400 Subject: [PATCH 6/8] Audio position update time should factor in speed The audio service continuously sends events checking the playback status every few seconds, depending on how much time is left until the end of the ayah. These update timings need to now factor in the speed, so if we are 3 seconds away from the end of the ayah and are playing at 1.5x, for example, checking after 3 seconds is too late (since we would have played some of the next ayah). --- .../labs/androidquran/service/AudioService.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 da40936e73..89b7c3fe9f 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 @@ -55,6 +55,7 @@ import android.support.v4.media.session.PlaybackStateCompat 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 @@ -405,6 +406,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, audioQueue = localAudioQueue.withUpdatedAudioRequest(playInfo) if (playInfo.playbackSpeed != audioRequest?.playbackSpeed) { processUpdatePlaybackSpeed(playInfo.playbackSpeed) + serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 200) } audioRequest = playInfo } @@ -570,14 +572,11 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, } notifyAyahChanged() if (maxAyahs >= updatedAyah + 1) { - var t = gaplessSuraData[updatedAyah + 1] - localPlayer.currentPosition - Timber.d("updateAudioPlayPosition postingDelayed after: %d", t) - if (t < 100) { - t = 100 - } else if (t > 10000) { - t = 10000 - } - serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, t.toLong()) + val timeDelta = gaplessSuraData[updatedAyah + 1] - localPlayer.currentPosition + val t = clamp(timeDelta, 100, 10000) + val tAccountingForSpeed = t / (audioRequest?.playbackSpeed ?: 1f) + Timber.d("updateAudioPlayPosition after: %d, speed %f", t, tAccountingForSpeed) + serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, tAccountingForSpeed.toLong()) } else if (maxAyahs == updatedAyah) { serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 150) } From 9fefe6602e323e5936b1d5d7e025e3de63875e1a Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sat, 6 Jan 2024 22:08:12 +0400 Subject: [PATCH 7/8] Add speed option to audio settings panel --- .../labs/androidquran/ui/PagerActivity.java | 10 +++-- .../ui/fragment/AyahPlaybackFragment.kt | 39 ++++++++++++------- .../androidquran/view/AudioStatusBar.java | 28 ++++++++----- .../res/layout-ar-land-v17/audio_panel.xml | 1 + .../main/res/layout-ar-land/audio_panel.xml | 1 + .../main/res/layout-ar-v17/audio_panel.xml | 1 + app/src/main/res/layout-ar/audio_panel.xml | 1 + app/src/main/res/layout-land/audio_panel.xml | 1 + app/src/main/res/layout/audio_panel.xml | 1 + app/src/main/res/layout/play_each_verse.xml | 4 +- .../main/res/layout/play_set_of_verses.xml | 4 +- app/src/main/res/layout/playback_speed.xml | 35 +++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 13 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 app/src/main/res/layout/playback_speed.xml 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 430da9f023..e9ffcf2cda 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 @@ -1564,13 +1564,13 @@ public void proceedWithDownload(Intent downloadIntent) { public void handlePlayback(AudioRequest request) { needsPermissionToDownloadOver3g = true; - audioStatusBar.resetSpeed(); final Intent intent = new Intent(this, AudioService.class); 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); } @@ -1586,7 +1586,7 @@ public void onPausePressed() { } @Override - public void setPlayBackSpeed(float speed) { + public void setPlaybackSpeed(float speed) { if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1645,7 +1645,8 @@ public void onShowQariList() { public boolean updatePlayOptions(int rangeRepeat, int verseRepeat, - boolean enforceRange) { + boolean enforceRange, + float playbackSpeed) { if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1653,7 +1654,7 @@ public boolean updatePlayOptions(int rangeRepeat, verseRepeat, rangeRepeat, enforceRange, - lastAudioRequest.getPlaybackSpeed(), + playbackSpeed, lastAudioRequest.getShouldStream(), lastAudioRequest.getAudioPathInfo()); Intent i = new Intent(this, AudioService.class); @@ -1663,6 +1664,7 @@ public boolean updatePlayOptions(int rangeRepeat, lastAudioRequest = updatedAudioRequest; audioStatusBar.setRepeatCount(verseRepeat); + audioStatusBar.setSpeed(playbackSpeed); return true; } else { return false; diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt index db90ed4208..649190224f 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt +++ b/app/src/main/java/com/quran/labs/androidquran/ui/fragment/AyahPlaybackFragment.kt @@ -35,6 +35,7 @@ class AyahPlaybackFragment : AyahActionFragment() { private var shouldEnforce = false private var rangeRepeatCount = 0 private var verseRepeatCount = 0 + private var currentSpeed = 1.0f private lateinit var applyButton: Button private lateinit var startSuraSpinner: QuranSpinner @@ -43,6 +44,7 @@ class AyahPlaybackFragment : AyahActionFragment() { private lateinit var endingAyahSpinner: QuranSpinner private lateinit var repeatVersePicker: NumberPicker private lateinit var repeatRangePicker: NumberPicker + private lateinit var playbackSpeedPicker: NumberPicker private lateinit var restrictToRange: CheckBox private lateinit var startAyahAdapter: ArrayAdapter @@ -76,6 +78,7 @@ class AyahPlaybackFragment : AyahActionFragment() { applyButton.setOnClickListener(onClickListener) repeatVersePicker = view.findViewById(R.id.repeat_verse_picker) repeatRangePicker = view.findViewById(R.id.repeat_range_picker) + playbackSpeedPicker = view.findViewById(R.id.playback_speed_picker) val context = requireContext() val isArabicNames = QuranSettings.getInstance(context).isArabicNames @@ -91,18 +94,15 @@ class AyahPlaybackFragment : AyahActionFragment() { } values[MAX_REPEATS] = getString(R.string.infinity) if (isArabicNames) { - repeatVersePicker.formatter = NumberPicker.Formatter { value: Int -> arFormat(value) } - repeatRangePicker.formatter = NumberPicker.Formatter { value: Int -> arFormat(value) } - val typeface = TypefaceManager.getHeaderFooterTypeface(context) - repeatVersePicker.typeface = typeface - repeatVersePicker.setSelectedTypeface(typeface) - repeatRangePicker.typeface = typeface - repeatRangePicker.setSelectedTypeface(typeface) - // Use larger text size since KFGQPC font is small - repeatVersePicker.setSelectedTextSize(R.dimen.arabic_number_picker_selected_text_size) - repeatRangePicker.setSelectedTextSize(R.dimen.arabic_number_picker_selected_text_size) - repeatVersePicker.setTextSize(R.dimen.arabic_number_picker_text_size) - repeatRangePicker.setTextSize(R.dimen.arabic_number_picker_text_size) + listOf(repeatVersePicker, repeatRangePicker, playbackSpeedPicker).forEach { + it.formatter = NumberPicker.Formatter { value: Int -> arFormat(value) } + val typeface = TypefaceManager.getHeaderFooterTypeface(context) + it.typeface = typeface + it.setSelectedTypeface(typeface) + // Use larger text size since KFGQPC font is small + it.setSelectedTextSize(R.dimen.arabic_number_picker_selected_text_size) + it.setTextSize(R.dimen.arabic_number_picker_text_size) + } } repeatVersePicker.minValue = 1 repeatVersePicker.maxValue = MAX_REPEATS + 1 @@ -112,6 +112,10 @@ class AyahPlaybackFragment : AyahActionFragment() { repeatRangePicker.displayedValues = values repeatRangePicker.value = defaultRangeRepeat repeatVersePicker.value = defaultVerseRepeat + playbackSpeedPicker.minValue = 1 + playbackSpeedPicker.maxValue = SPEEDS.size + playbackSpeedPicker.displayedValues = SPEEDS.map { numberFormat.format(it) }.toTypedArray() + playbackSpeedPicker.value = DEFAULT_SPEED_INDEX + 1 repeatRangePicker.setOnValueChangedListener { _: NumberPicker?, _: Int, newVal: Int -> if (newVal > 1) { // whenever we want to repeat the range, we have to enable restrictToRange @@ -189,7 +193,7 @@ class AyahPlaybackFragment : AyahActionFragment() { val enforceRange = restrictToRange.isChecked var updatedRange = false - val speed = 1.0f // TODO: expose a setting within this fragment also + val speed = SPEEDS[playbackSpeedPicker.value - 1] if (currentStart != decidedStart || currentEnding != decidedEnd) { // different range or not playing, so make a new request updatedRange = true @@ -197,9 +201,9 @@ class AyahPlaybackFragment : AyahActionFragment() { currentStart, currentEnding, page, verseRepeat, rangeRepeat, enforceRange, speed ) - } else if (shouldEnforce != enforceRange || rangeRepeatCount != rangeRepeat || verseRepeatCount != verseRepeat) { + } else if (shouldEnforce != enforceRange || rangeRepeatCount != rangeRepeat || verseRepeatCount != verseRepeat || currentSpeed != speed) { // can just update repeat settings - if (!context.updatePlayOptions(rangeRepeat, verseRepeat, enforceRange) + if (!context.updatePlayOptions(rangeRepeat, verseRepeat, enforceRange, speed) ) { // audio stopped in the process, let's start it context.playFromAyah( @@ -304,6 +308,7 @@ class AyahPlaybackFragment : AyahActionFragment() { if (lastRequest != lastSeenAudioRequest) { verseRepeatCount = lastRequest.repeatInfo rangeRepeatCount = lastRequest.rangeRepeatInfo + currentSpeed = lastRequest.playbackSpeed shouldEnforce = lastRequest.enforceBounds } else { shouldReset = false @@ -325,6 +330,7 @@ class AyahPlaybackFragment : AyahActionFragment() { } rangeRepeatCount = 0 verseRepeatCount = 0 + currentSpeed = 1.0f decidedStart = null decidedEnd = null applyButton.setText(R.string.play_apply_and_play) @@ -348,6 +354,7 @@ class AyahPlaybackFragment : AyahActionFragment() { restrictToRange.isChecked = shouldEnforce repeatRangePicker.value = rangeRepeatCount + 1 repeatVersePicker.value = verseRepeatCount + 1 + playbackSpeedPicker.value = SPEEDS.indexOf(currentSpeed) + 1 } } } @@ -356,5 +363,7 @@ class AyahPlaybackFragment : AyahActionFragment() { private val ITEM_LAYOUT = R.layout.sherlock_spinner_item private val ITEM_DROPDOWN_LAYOUT = R.layout.sherlock_spinner_dropdown_item private const val MAX_REPEATS = 25 + private val SPEEDS = listOf(0.5f, 0.75f, 1.0f, 1.25f, 1.5f) + private const val DEFAULT_SPEED_INDEX = 2 } } 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 19a17c2889..053889af7f 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 @@ -54,7 +54,7 @@ public class AudioStatusBar extends LeftToRightLinearLayout { private Qari currentQari; private int currentRepeat = 0; - private int defaultSpeedIndex = 2; + private final int defaultSpeedIndex = 2; private int currentSpeedIndex = defaultSpeedIndex; private final float[] speeds = { 0.5f, 0.75f, 1f, 1.25f, 1.5f}; private float currentSpeed = speeds[currentSpeedIndex]; @@ -80,7 +80,7 @@ public interface AudioBarListener { void onNextPressed(); void onPreviousPressed(); void onStopPressed(); - void setPlayBackSpeed(float speed); + void setPlaybackSpeed(float speed); void onCancelPressed(boolean stopDownload); void setRepeatCount(int repeatCount); void onAcceptPressed(); @@ -196,9 +196,14 @@ public QariItem getAudioInfo() { return QariItem.Companion.fromQari(context, currentQari); } - public void resetSpeed() { - currentSpeedIndex = defaultSpeedIndex; - updateSpeedButtonText(); + public void setSpeed(float speed) { + for (int i = 0; i < speeds.length; i++) { + if (speeds[i] == speed) { + currentSpeedIndex = i; + updateSpeedButtonText(); + return; + } + } } public void setProgress(int progress) { @@ -529,7 +534,7 @@ private void incrementRepeat() { updateRepeatButtonText(); } - private void updatePlayBackSpeed() { + private void updatePlaybackSpeed() { currentSpeedIndex = (currentSpeedIndex + 1) % speeds.length; currentSpeed = speeds[currentSpeedIndex]; updateSpeedButtonText(); @@ -555,7 +560,12 @@ private void updateSpeedButtonText(){ } else { str = String.valueOf(currentSpeed); } - speedButton.setText(str); + + post(() -> { + if (speedButton != null) { + speedButton.setText(str); + } + }); } public void setRepeatCount(int repeatCount) { @@ -606,8 +616,8 @@ public void onClick(View view) { } else if (tag == R.drawable.ic_next) { audioBarListener.onNextPressed(); } else if (tag == R.drawable.ic_speed) { - updatePlayBackSpeed(); - audioBarListener.setPlayBackSpeed(currentSpeed); + updatePlaybackSpeed(); + audioBarListener.setPlaybackSpeed(currentSpeed); } else if (tag == R.drawable.ic_previous) { audioBarListener.onPreviousPressed(); } else if (tag == R.drawable.ic_repeat) { diff --git a/app/src/main/res/layout-ar-land-v17/audio_panel.xml b/app/src/main/res/layout-ar-land-v17/audio_panel.xml index 311ec21ba3..d6cc338f03 100644 --- a/app/src/main/res/layout-ar-land-v17/audio_panel.xml +++ b/app/src/main/res/layout-ar-land-v17/audio_panel.xml @@ -86,6 +86,7 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 490dbc7fbd..6bc12f4030 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -346,6 +346,7 @@ Pause Stop Next + Playback Speed: 1 time From 49d805e70310064ce3f1624a2bad2ad4acba21e0 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sat, 6 Jan 2024 23:54:17 +0400 Subject: [PATCH 8/8] Translate playback speed string --- app/src/main/res/values-ar/strings.xml | 1 + app/src/main/res/values-az/strings.xml | 1 + app/src/main/res/values-bs/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fa/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-hr/strings.xml | 1 + app/src/main/res/values-hu/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-kk/strings.xml | 1 + app/src/main/res/values-ku/strings.xml | 1 + app/src/main/res/values-ms/strings.xml | 1 + app/src/main/res/values-nl/strings.xml | 1 + app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-sq/strings.xml | 1 + app/src/main/res/values-sr/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-th/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 1 + app/src/main/res/values-ug/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values-uz/strings.xml | 1 + app/src/main/res/values-vi/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 28 files changed, 28 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 753d62a2cd..58f3269629 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -243,6 +243,7 @@ الوضع العربي تشغيل إيقاف + سرعة التشغيل: تم تحسين الصور الخاصه بالتابلت. هل تود تحميلها الان؟ رتب حسب التصنيفات diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 30a1b6b71b..c33a686a62 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -258,6 +258,7 @@ Dinle Duraklat Durdur + Oynatma Sürati: Sonraki diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 0fa7371c9f..40747dc9a1 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -276,6 +276,7 @@ Pokreni Pauziraj Stani + Brzina reprodukcije: Sljedeći diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ffc587ed00..5ac54791a4 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -288,6 +288,7 @@ Anwenden und Abspielen Abspielen Stoppen + Wiedergabegeschwindigkeit: einmal diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7858a9be06..5c93d680e3 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -161,6 +161,7 @@ Aplicar y reproducir Reproducir Detener + Velocidad de reproducción: Versículo copiado diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 8d7214050f..f3a8b87b2e 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -217,6 +217,7 @@ قبلی مکث توقف + سرعت پخش: بعدی حافظه‌ای که تمایل دارید فایل‌ها روی آن ذخیره شود را انتخاب نمایید diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 481a38b7a6..803e141bcb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -238,6 +238,7 @@ Écouter Pause Stopper + Vitesse de lecture : Suivant diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index b55e7bac1a..a8d5d02902 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -276,6 +276,7 @@ Pokreni Pauziraj Stani + Brzina reprodukcije: Sljedeći diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 35a08fe5ab..a50aa62aae 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -211,6 +211,7 @@ Alkalmaz és lejátszás Lejátszás Leállítás + Lejátszási sebesség: egyszer diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index a3a4edc90d..2729dc7202 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -263,6 +263,7 @@ Putar Jeda Stop + वापसी की गति: Selanjutnya diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 7d353ec686..ef2a4af537 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -31,6 +31,7 @@ Precedente Pausa Fermare + Velocità di riproduzione: Prossimo Undo Tag diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 24cedcb0b8..c034f19674 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -266,6 +266,7 @@ Тыңдау Кідірту Тоқтату + Жүктеу жылдамдылығы: Келесі diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml index 6186cd0aab..4045794495 100644 --- a/app/src/main/res/values-ku/strings.xml +++ b/app/src/main/res/values-ku/strings.xml @@ -272,6 +272,7 @@ گوێبگرە ڕاوەستان وەستان + سرعة التشغيل: دواتر دەست بکە بە پەخشکردنەوە لە: سەرەتای لاپەڕە diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 5d69155c37..67c5da76d5 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -234,6 +234,7 @@ Mainkan Pause Hentikan + درجة سرعة التشغيل: Seterus diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 826c26f02f..eb224010b9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -375,6 +375,7 @@ Afspelen Pauzeren Onderbreken + Afspeelsnelheid: Volgende diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 43df4b0538..dcf385ea46 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -266,6 +266,7 @@ Odtwórz Pauza Zatrzymać + Prędkość odtwarzania: Następny diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e283b0fab6..758f9d7146 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,6 +283,7 @@ escolher um leitor-qari diferente. Clique play para baixar e reproduzir a págin Ouça Pausa Parada + Velocidade de reprodução: Próximo Iniciar a reprodução a partir de: Início da página diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c91cfb7e8c..46aae4d194 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -314,6 +314,7 @@ Воспроизвести Пауза Приостановить + Скорость воспроизведения: Следующий diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index d912cba534..9bdd514e44 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -265,6 +265,7 @@ Dëgjo. Pauzë Ndalo. + Shpejtesia e Ripertrajtimit: Në vazhdim diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index e0b7005b1a..3218ca037f 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -276,6 +276,7 @@ Pokreni Pauziraj Stani + Брзина репродукције: Sledeći diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 4dcbee5e99..2133a5d192 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -265,6 +265,7 @@ Spela upp Pausa Stoppa + Uppspelningshastighet: Nästa diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 41f006d4fe..8e6cf6e4e1 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -256,6 +256,7 @@ เล่น หยุด หยุด + ความเร็วในการเล่นเสียง: ต่อไป diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 30a1b6b71b..6e56bc017f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -258,6 +258,7 @@ Dinle Duraklat Durdur + Oynatma Hızı: Sonraki diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index ce38c24faf..4efbfb686f 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -276,6 +276,7 @@ پۈتكۈل سەھىپەدىن ئىزدە ئالدىنقى توختا + Ujinu uri vifujio: قوش بەت مايىللىقى قوش بەت ھالىتىدىكى قۇرئان ۋە تەرجىمە تەرجىمە بار قوش بەت ھالىتىدە ، قۇرئان بېتى ۋە تەرجىمە كۆرۈنىدۇ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 57147b3681..c13f1b5640 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -267,6 +267,7 @@ Слухати Пауза Зупинити + Llais yn ôl: Наступний diff --git a/app/src/main/res/values-uz/strings.xml b/app/src/main/res/values-uz/strings.xml index 35efd2ea7e..69295207f1 100644 --- a/app/src/main/res/values-uz/strings.xml +++ b/app/src/main/res/values-uz/strings.xml @@ -291,6 +291,7 @@ Chalish Pauza Toʻxtatish + Tezlikni tomosha qilish: Keyingi diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 2df98f4e9c..d5cae4af12 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -293,6 +293,7 @@ Phát Nghỉ Dừng + Tốc độ phát lại: Tiếp diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2ce0b5f1b3..283e6c34d3 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -268,6 +268,7 @@ 播放 暂停 停止 + 播放速度: 下一页 开始播放从。 页首