Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] share ayah as audio #2011

Closed
wants to merge 13 commits into from
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@
android:authorities="@string/file_authority"
android:grantUriPermissions="true"
android:exported="false">
tools:replace="android:authorities">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did we need to add this?

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
android:resource="@xml/file_paths"
tools:replace="android:resource" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

</provider>

<receiver android:name="androidx.media.session.MediaButtonReceiver">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ constructor(private val quranDisplayData: QuranDisplayData,
lastAudioRequest?.let { play(it) }
}

private fun getDownloadIntent(context: Context, request: AudioRequest): Intent? {
fun getDownloadIntent(context: Context, request: AudioRequest): Intent? {
val qari = request.qari
val audioPathInfo = request.audioPathInfo
val path = audioPathInfo.localDirectory
Expand Down
138 changes: 121 additions & 17 deletions app/src/main/java/com/quran/labs/androidquran/ui/PagerActivity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.quran.labs.androidquran.ui;

import static com.quran.labs.androidquran.database.DatabaseUtils.closeCursor;
import static com.quran.labs.androidquran.ui.helpers.SlidingPagerAdapter.AUDIO_PAGE;
import static com.quran.labs.androidquran.ui.helpers.SlidingPagerAdapter.TAG_PAGE;
import static com.quran.labs.androidquran.ui.helpers.SlidingPagerAdapter.TRANSLATION_PAGE;
Expand All @@ -15,13 +16,17 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.Menu;
Expand Down Expand Up @@ -70,14 +75,17 @@
import com.quran.labs.androidquran.common.LocalTranslationDisplaySort;
import com.quran.labs.androidquran.common.QuranAyahInfo;
import com.quran.labs.androidquran.common.audio.model.QariItem;
import com.quran.labs.androidquran.dao.audio.AudioPathInfo;
import com.quran.labs.androidquran.dao.audio.AudioRequest;
import com.quran.labs.androidquran.data.Constants;
import com.quran.labs.androidquran.data.QuranDataProvider;
import com.quran.labs.androidquran.data.QuranDisplayData;
import com.quran.labs.androidquran.database.SuraTimingDatabaseHandler;
import com.quran.labs.androidquran.database.TranslationsDBAdapter;
import com.quran.labs.androidquran.di.component.activity.PagerActivityComponent;
import com.quran.labs.androidquran.di.module.activity.PagerActivityModule;
import com.quran.labs.androidquran.di.module.fragment.QuranPageModule;
import com.quran.labs.androidquran.feature.audio.util.AudioShareUtils;
import com.quran.labs.androidquran.model.bookmark.BookmarkModel;
import com.quran.labs.androidquran.model.translation.ArabicDatabaseUtils;
import com.quran.labs.androidquran.presenter.audio.AudioPresenter;
Expand Down Expand Up @@ -123,8 +131,10 @@
import com.quran.reading.common.AudioEventPresenter;
import com.quran.reading.common.ReadingEventPresenter;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand All @@ -141,6 +151,8 @@
import io.reactivex.rxjava3.observers.DisposableObserver;
import io.reactivex.rxjava3.observers.DisposableSingleObserver;
import io.reactivex.rxjava3.schedulers.Schedulers;
import kotlin.TuplesKt;
import kotlin.jvm.internal.Intrinsics;
import timber.log.Timber;

/**
Expand Down Expand Up @@ -216,8 +228,10 @@ public class PagerActivity extends AppCompatActivity implements
private int defaultNavigationBarColor;
private boolean isSplitScreen = false;

@Nullable private QuranAyahInfo lastSelectedTranslationAyah;
@Nullable private LocalTranslation[] lastActivatedLocalTranslations;
@Nullable
private QuranAyahInfo lastSelectedTranslationAyah;
@Nullable
private LocalTranslation[] lastActivatedLocalTranslations;

private PagerActivityComponent pagerActivityComponent;

Expand Down Expand Up @@ -249,6 +263,12 @@ public class PagerActivity extends AppCompatActivity implements

private final PagerHandler handler = new PagerHandler(this);

private ArrayList<String> audioCacheFilePaths = new ArrayList<>();
private SuraAyah selectedStartSuraAyah = null;
private SuraAyah selectedEndSuraAyah = null;
private QariItem selectedQari = null;
Comment on lines +267 to +269
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may we not add these back? I did a bunch of work to clean these types of variables from here, and while it's not done yet, we're managing translations without the direct usage of variables like this.

this class is massive and it's been slow trying to clean it up, but at least I want to avoid adding more stuff to it while we clean it up and make it a reasonable size.



private static class PagerHandler extends Handler {
private final WeakReference<PagerActivity> activity;

Expand Down Expand Up @@ -285,12 +305,21 @@ public void onCreate(Bundle savedInstanceState) {
isSplitScreen = quranSettings.isQuranSplitWithTranslation();
audioEventPresenterBridge = new AudioEventPresenterBridge(
audioEventPresenter,
suraAyah -> { onAudioPlaybackAyahChanged(suraAyah); return null; }
suraAyah -> {
onAudioPlaybackAyahChanged(suraAyah);
return null;
}
);
readingEventPresenterBridge = new ReadingEventPresenterBridge(
readingEventPresenter,
() -> { onPageClicked(); return null; },
ayahSelection -> { onAyahSelectionChanged(ayahSelection); return null; }
() -> {
onPageClicked();
return null;
},
ayahSelection -> {
onAyahSelectionChanged(ayahSelection);
return null;
}
);

// remove the window background to avoid overdraw. note that, per Romain's blog, this is
Expand Down Expand Up @@ -418,7 +447,8 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse
} else if (position == barPos - 1 || position == barPos + 1) {
// Swiping to previous or next ViewPager page (i.e. next or previous quran page)
final SelectionIndicator updatedSelectionIndicator =
SelectionIndicatorKt.withXScroll(selectionIndicator, viewPager.getWidth() - positionOffsetPixels);
SelectionIndicatorKt.withXScroll(selectionIndicator,
viewPager.getWidth() - positionOffsetPixels);
readingEventPresenterBridge.withSelectionIndicator(updatedSelectionIndicator);
} else {
readingEventPresenterBridge.clearSelectedAyah();
Expand Down Expand Up @@ -538,8 +568,14 @@ public void onPageSelected(int position) {
this::getCurrentPage,
() -> audioStatusBar,
() -> ayahToolBar,
ayah -> { ensurePage(ayah.sura, ayah.ayah); return null; },
sliderPage -> { showSlider(slidingPagerAdapter.getPagePosition(sliderPage)); return null; }
ayah -> {
ensurePage(ayah.sura, ayah.ayah);
return null;
},
sliderPage -> {
showSlider(slidingPagerAdapter.getPagePosition(sliderPage));
return null;
}
));
}

Expand Down Expand Up @@ -1368,16 +1404,16 @@ private void ensurePage(int sura, int ayah) {
private void requestTranslationsList() {
compositeDisposable.add(
Single.fromCallable(() ->
translationsDBAdapter.getTranslations())
translationsDBAdapter.getTranslations())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<List<LocalTranslation>>() {
@Override
public void onSuccess(@NonNull List<LocalTranslation> translationList) {
final List<LocalTranslation> sortedTranslations = new ArrayList<>(translationList);
Collections.sort(sortedTranslations, new LocalTranslationDisplaySort());
Collections.sort(sortedTranslations, new LocalTranslationDisplaySort());

int items = sortedTranslations.size();
int items = sortedTranslations.size();
String[] titles = new String[items];
for (int i = 0; i < items; i++) {
LocalTranslation item = sortedTranslations.get(i);
Expand All @@ -1394,7 +1430,8 @@ public void onSuccess(@NonNull List<LocalTranslation> translationList) {
if (currentActiveTranslationsFilesNames.isEmpty() && items > 0) {
currentActiveTranslationsFilesNames = new HashSet<>();
for (int i = 0; i < items; i++) {
currentActiveTranslationsFilesNames.add(sortedTranslations.get(i).getFilename());
currentActiveTranslationsFilesNames.add(
sortedTranslations.get(i).getFilename());
}
}
activeTranslationsFilesNames = currentActiveTranslationsFilesNames;
Expand Down Expand Up @@ -1427,7 +1464,8 @@ public void onSuccess(@NonNull Boolean isBookmarked) {
if (sura == null || ayah == null) {
// page bookmark
bookmarksCache.put(page, isBookmarked);
bookmarksMenuItem.setIcon(isBookmarked ? com.quran.labs.androidquran.common.toolbar.R.drawable.ic_favorite : com.quran.labs.androidquran.common.toolbar.R.drawable.ic_not_favorite);
bookmarksMenuItem.setIcon(
isBookmarked ? com.quran.labs.androidquran.common.toolbar.R.drawable.ic_favorite : com.quran.labs.androidquran.common.toolbar.R.drawable.ic_not_favorite);
} else {
// ayah bookmark
SuraAyah suraAyah = new SuraAyah(sura, ayah);
Expand Down Expand Up @@ -1476,7 +1514,8 @@ private void refreshBookmarksMenu() {
bookmarked = bookmarksCache.get(page - 1);
}

menuItem.setIcon(bookmarked ? com.quran.labs.androidquran.common.toolbar.R.drawable.ic_favorite : com.quran.labs.androidquran.common.toolbar.R.drawable.ic_not_favorite);
menuItem.setIcon(
bookmarked ? com.quran.labs.androidquran.common.toolbar.R.drawable.ic_favorite : com.quran.labs.androidquran.common.toolbar.R.drawable.ic_not_favorite);
} else {
supportInvalidateOptionsMenu();
}
Expand Down Expand Up @@ -1517,7 +1556,7 @@ private void playFromAyah(int page, int startSura, int startAyah) {
final SuraAyah start = new SuraAyah(startSura, 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;
final SuraAyah ending = (end == null || start.equals(end) || start.after(end)) ? null : end;
playFromAyah(start, ending, page, 0, 0, ending != null);
}

Expand Down Expand Up @@ -1760,6 +1799,8 @@ public boolean onMenuItemClick(MenuItem item) {
shareAyahLink(startSuraAyah, endSuraAyah);
} else if (itemId == com.quran.labs.androidquran.common.toolbar.R.id.cab_share_ayah_text) {
shareAyah(startSuraAyah, endSuraAyah, false);
} else if (itemId == com.quran.labs.androidquran.common.toolbar.R.id.cab_share_ayah_audio) {
shareAyahAudio(startSuraAyah, endSuraAyah);
} else if (itemId == com.quran.labs.androidquran.common.toolbar.R.id.cab_copy_ayah) {
shareAyah(startSuraAyah, endSuraAyah, true);
} else {
Expand Down Expand Up @@ -1796,7 +1837,8 @@ private void shareAyah(SuraAyah start, SuraAyah end, final boolean isCopy) {
if (isCopy) {
shareUtil.copyToClipboard(this, shareText);
} else {
shareUtil.shareViaIntent(this, shareText, com.quran.labs.androidquran.common.toolbar.R.string.share_ayah_text);
shareUtil.shareViaIntent(this, shareText,
com.quran.labs.androidquran.common.toolbar.R.string.share_ayah_text);
}
}

Expand Down Expand Up @@ -1825,7 +1867,8 @@ public void shareAyahLink(SuraAyah start, SuraAyah end) {
.subscribeWith(new DisposableSingleObserver<String>() {
@Override
public void onSuccess(@NonNull String url) {
shareUtil.shareViaIntent(PagerActivity.this, url, com.quran.labs.androidquran.common.toolbar.R.string.share_ayah);
shareUtil.shareViaIntent(PagerActivity.this, url,
com.quran.labs.androidquran.common.toolbar.R.string.share_ayah);
dismissProgressDialog();
}

Expand All @@ -1837,6 +1880,67 @@ public void onError(@NonNull Throwable e) {
);
}

public void shareAyahAudio(SuraAyah start, SuraAyah end) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move all this code out of here - somewhere in that feature/audiosharing module you'll make instead so we don't add more lines to this already massive class

audioCacheFilePaths.clear();

kotlin.Pair pair2 = getReorderedAyatPair(start, end);
selectedStartSuraAyah = (SuraAyah) pair2.component1();
selectedEndSuraAyah = (SuraAyah) pair2.component2();
Comment on lines +1886 to +1888
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

val (startSuraAyah, endSuraAyah) = getReorderedAyatPair(start, end)


selectedQari = audioStatusBar.getAudioInfo();
AudioPathInfo audioPathInfo = audioUtils.getLocalAudioPathInfo(selectedQari);

assert audioPathInfo != null;
boolean gaplessDatabaseExists = audioPathInfo.getGaplessDatabase() != null;

if (gaplessDatabaseExists) {
if (audioFilesExist(audioPathInfo)) {
AudioShareUtils audioShareUtils = new AudioShareUtils();
String path = audioShareUtils.createSharableAudioFile(this, selectedStartSuraAyah,
selectedEndSuraAyah, selectedQari, audioPathInfo.getUrlFormat(),
audioPathInfo.getGaplessDatabase());
if(path != null && !path.isEmpty()){
shareAudioSegment(path);
}else{
Toast.makeText(this, "could not share audio ayah", Toast.LENGTH_SHORT).show();
}
} else {
requestDownload(audioPathInfo);
}
}
}

private kotlin.Pair getReorderedAyatPair(SuraAyah start, SuraAyah end) {
kotlin.Pair pair;
if (start.compareTo(end) <= 0) {
pair = TuplesKt.to(start, end);
} else {
Timber.Forest.e(
new IllegalStateException("End isn't larger than the start: " + start + " to " + end));
pair = TuplesKt.to(end, start);
}
return pair;
}
Comment on lines +1913 to +1923
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private kotlin.Pair getReorderedAyatPair(SuraAyah start, SuraAyah end) {
kotlin.Pair pair;
if (start.compareTo(end) <= 0) {
pair = TuplesKt.to(start, end);
} else {
Timber.Forest.e(
new IllegalStateException("End isn't larger than the start: " + start + " to " + end));
pair = TuplesKt.to(end, start);
}
return pair;
}
private kotlin.Pair<SuraAyah, SuraAyah> getReorderedAyatPair(SuraAyah start, SuraAyah end) {
return if (start >= end) {
start to end
} else {
end to start
}
}

when we move a lot of this logic, we can also change kotlin.Pair to just pair

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggestion would be good if PagerActivity was in kotlin. I tried to simply it but it could not get simpler in java


private boolean audioFilesExist(AudioPathInfo audioPathInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty sure we have something already doing this somewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, in AudioUtils. I felt putting the code in a method will help on readability about whats going on

return audioUtils.haveAllFiles(audioPathInfo.getUrlFormat(), audioPathInfo.getLocalDirectory(),
selectedStartSuraAyah, selectedEndSuraAyah, true);
}

private void shareAudioSegment(String path) {
shareUtil.shareAudioFileIntent(PagerActivity.this, new File(path));
}

private void requestDownload(AudioPathInfo audioPathInfo) {
AudioRequest audioRequest = new AudioRequest(
selectedStartSuraAyah, selectedEndSuraAyah, selectedQari, 0, 0, true, false, audioPathInfo);

Intent downloadIntent = audioPresenter.getDownloadIntent(this, audioRequest);
if (downloadIntent != null) {
handleRequiredDownload(downloadIntent);
}
}

private void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/com/quran/labs/androidquran/util/AudioUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.quran.data.model.SuraAyah
import com.quran.labs.androidquran.common.audio.model.AudioConfiguration
import com.quran.labs.androidquran.common.audio.model.QariItem
import com.quran.labs.androidquran.common.audio.util.QariUtil
import com.quran.labs.androidquran.dao.audio.AudioPathInfo
import com.quran.labs.androidquran.service.AudioService
import timber.log.Timber
import java.io.File
Expand Down Expand Up @@ -283,6 +284,21 @@ class AudioUtils @Inject constructor(
}
}

fun getLocalAudioPathInfo(qari: QariItem): AudioPathInfo? {
val localPath = getLocalQariUrl(qari)
if (localPath != null) {
val databasePath = getQariDatabasePathIfGapless(qari)
val urlFormat = if (databasePath.isNullOrEmpty()) {
localPath + File.separator + "%d" + File.separator +
"%d" + AUDIO_EXTENSION
} else {
localPath + File.separator + "%03d" + AUDIO_EXTENSION
}
return AudioPathInfo(urlFormat, localPath, databasePath)
}
return null
}

companion object {
const val ZIP_EXTENSION = ".zip"
const val AUDIO_EXTENSION = ".mp3"
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/com/quran/labs/androidquran/util/ShareUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.quran.data.model.QuranText
import com.quran.labs.androidquran.BuildConfig
import com.quran.labs.androidquran.R
import com.quran.labs.androidquran.common.LocalTranslation
import com.quran.labs.androidquran.common.QuranAyahInfo
import com.quran.labs.androidquran.data.QuranDisplayData
import com.quran.labs.androidquran.model.translation.ArabicDatabaseUtils
import com.quran.labs.androidquran.ui.util.ToastCompat
import dagger.Reusable
import java.io.File
import java.text.NumberFormat
import java.util.Locale
import javax.inject.Inject
Expand Down Expand Up @@ -124,4 +128,15 @@ class ShareUtil @Inject internal constructor(private val quranDisplayData: Quran
append("]")
}
}


fun shareAudioFileIntent(activity: Activity, file: File) {
val authorities = BuildConfig.APPLICATION_ID + ".fileprovider"
val uri = FileProvider.getUriForFile(activity, authorities, file)
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
shareIntent.type = "audio/mp3"
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
activity.startActivity(Intent.createChooser(shareIntent, activity.getString(R.string.share_audio_file_title)))
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@

<!-- long press -->
<string name="ayah_copied_popup">تم نسخ الآية</string>
<string name="share_audio_file_title">Share Audio File</string>

<!-- bookmarks & tags contextual action bar -->
<string name="tag_bookmark">تصنيف المرجعية</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-az/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@

<!-- long press -->
<string name="ayah_copied_popup">Âyet kopyalandı</string>
<string name="share_audio_file_title">Share Audio File</string>

<!-- bookmarks & tags contextual action bar -->
<string name="tag_bookmark">Sayfa işareti etiketi</string>
Expand Down
Loading