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 df7e308080..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 @@ -398,11 +399,15 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, processStopRequest() } else if (ACTION_REWIND == action) { processRewindRequest() - } 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) + serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 200) + } audioRequest = playInfo } } else { @@ -567,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) } @@ -678,6 +680,15 @@ 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 -> + params.setSpeed(speed) + player?.playbackParams = params + } + } + } + private fun processSkipRequest() { if (audioRequest == null) { return @@ -1048,6 +1059,9 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, ) player.start() state = State.Playing + audioRequest?.playbackSpeed?.let { speed -> + processUpdatePlaybackSpeed(speed) + } serviceHandler.sendEmptyMessageDelayed(MSG_UPDATE_AUDIO_POS, 200) } @@ -1351,10 +1365,7 @@ 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" @@ -1362,7 +1373,7 @@ class AudioService : Service(), OnCompletionListener, OnPreparedListener, const val ACTION_SKIP = "com.quran.labs.androidquran.action.SKIP" const val ACTION_REWIND = "com.quran.labs.androidquran.action.REWIND" 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 @@ -1378,6 +1389,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 5154c66d05..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 @@ -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); } } @@ -1569,6 +1570,7 @@ public void handlePlayback(AudioRequest request) { intent.putExtra(AudioService.EXTRA_PLAY_INFO, request); lastAudioRequest = request; audioStatusBar.setRepeatCount(request.getRepeatInfo()); + audioStatusBar.setSpeed(request.getPlaybackSpeed()); audioStatusBar.switchMode(AudioStatusBar.LOADING_MODE); } @@ -1583,6 +1585,27 @@ public void onPausePressed() { audioStatusBar.switchMode(AudioStatusBar.PAUSED_MODE); } + @Override + public void setPlaybackSpeed(float 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 public void onNextPressed() { startService(audioUtils.getAudioIntent(this, @@ -1621,7 +1644,9 @@ public void onShowQariList() { } public boolean updatePlayOptions(int rangeRepeat, - int verseRepeat, boolean enforceRange) { + int verseRepeat, + boolean enforceRange, + float playbackSpeed) { if (lastAudioRequest != null) { final AudioRequest updatedAudioRequest = new AudioRequest(lastAudioRequest.getStart(), lastAudioRequest.getEnd(), @@ -1629,15 +1654,17 @@ public boolean updatePlayOptions(int rangeRepeat, verseRepeat, rangeRepeat, enforceRange, + playbackSpeed, 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; audioStatusBar.setRepeatCount(verseRepeat); + audioStatusBar.setSpeed(playbackSpeed); return true; } else { return false; @@ -1653,11 +1680,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..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,20 +193,21 @@ class AyahPlaybackFragment : AyahActionFragment() { val enforceRange = restrictToRange.isChecked var updatedRange = false + val speed = SPEEDS[playbackSpeedPicker.value - 1] 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) { + } 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( - currentStart, currentEnding, page, verseRepeat, rangeRepeat, enforceRange + currentStart, currentEnding, page, verseRepeat, rangeRepeat, enforceRange, speed ) } } @@ -303,6 +308,7 @@ class AyahPlaybackFragment : AyahActionFragment() { if (lastRequest != lastSeenAudioRequest) { verseRepeatCount = lastRequest.repeatInfo rangeRepeatCount = lastRequest.rangeRepeatInfo + currentSpeed = lastRequest.playbackSpeed shouldEnforce = lastRequest.enforceBounds } else { shouldReset = false @@ -324,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) @@ -347,6 +354,7 @@ class AyahPlaybackFragment : AyahActionFragment() { restrictToRange.isChecked = shouldEnforce repeatRangePicker.value = rangeRepeatCount + 1 repeatVersePicker.value = verseRepeatCount + 1 + playbackSpeedPicker.value = SPEEDS.indexOf(currentSpeed) + 1 } } } @@ -355,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 3e74737d25..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 @@ -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 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]; @DrawableRes private int itemBackground; private final boolean isRtl; private boolean isDualPageMode; @@ -65,6 +70,7 @@ 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; @@ -74,6 +80,7 @@ public interface AudioBarListener { void onNextPressed(); void onPreviousPressed(); void onStopPressed(); + void setPlaybackSpeed(float speed); void onCancelPressed(boolean stopDownload); void setRepeatCount(int repeatCount); void onAcceptPressed(); @@ -104,6 +111,7 @@ public AudioStatusBar(Context context, AttributeSet attrs, int defStyle) { this.context = context; repeatButton = new RepeatButton(context); + speedButton = new RepeatButton(context); Resources resources = getResources(); buttonWidth = resources.getDimensionPixelSize( R.dimen.audiobar_button_width); @@ -188,6 +196,16 @@ public QariItem getAudioInfo() { return QariItem.Companion.fromQari(context, currentQari); } + 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) { if (hasErrorText) { progressText.setText(R.string.downloading_title); @@ -462,7 +480,11 @@ private void showPlayingMode(boolean isPaused) { addButton(R.drawable.ic_next, 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); } @@ -512,8 +534,14 @@ private void incrementRepeat() { updateRepeatButtonText(); } + private void updatePlaybackSpeed() { + 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) { @@ -524,6 +552,22 @@ private void updateRepeatButtonText() { repeatButton.setText(str); } + private void updateSpeedButtonText(){ + currentSpeed = speeds[currentSpeedIndex]; + final String str; + if (currentSpeedIndex == 2) { + str = ""; + } else { + str = String.valueOf(currentSpeed); + } + + post(() -> { + if (speedButton != null) { + speedButton.setText(str); + } + }); + } + public void setRepeatCount(int repeatCount) { boolean updated = false; if (currentRepeat != repeatCount) { @@ -571,6 +615,9 @@ 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); } 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..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 @@ -49,7 +49,12 @@ class RepeatButton @JvmOverloads constructor( if (drawable != null) { val bounds = drawable.bounds if (bounds.width() > 0) { - textXPosition = viewWidth - (viewWidth - bounds.width()) / 2 + 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 new file mode 100644 index 0000000000..807b50d0ec --- /dev/null +++ b/app/src/main/res/drawable/ic_speed.xml @@ -0,0 +1,3 @@ + + + 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-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 @@ 播放 暂停 停止 + 播放速度: 下一页 开始播放从。 页首 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