From 4b6c4a923b250e179bbf0462c07c1b25a69d2562 Mon Sep 17 00:00:00 2001 From: ismailjones Date: Sat, 25 Apr 2020 17:13:47 -0400 Subject: [PATCH 1/5] Support Reordering Of Translations. Fixes #1179 --- app/build.gradle | 4 +- .../common/LocalTransationDiplaySort.kt | 8 + .../androidquran/common/LocalTranslation.kt | 3 +- .../common/TranslationMetadata.kt | 1 + .../dao/translation/Translation.kt | 3 +- .../dao/translation/TranslationItem.kt | 5 +- .../translation/TranslationItemDiplaySort.kt | 8 + .../database/TranslationsDBAdapter.java | 24 ++- .../database/TranslationsDBHelper.java | 37 ++++- .../translation/BaseTranslationPresenter.kt | 8 +- .../TranslationManagerPresenter.java | 2 +- .../labs/androidquran/ui/PagerActivity.java | 16 +- .../ui/TranslationManagerActivity.java | 94 ++++++++++- .../ui/adapter/TranslationsAdapter.java | 153 ++++++++++++++++-- .../ui/translation/TranslationView.java | 24 ++- .../labs/androidquran/util/TranslationUtil.kt | 4 +- .../res/drawable-hdpi/arrow_circle_down.png | Bin 0 -> 763 bytes .../res/drawable-hdpi/arrow_circle_up.png | Bin 0 -> 760 bytes .../res/drawable-mdpi/arrow_circle_down.png | Bin 0 -> 519 bytes .../res/drawable-mdpi/arrow_circle_up.png | Bin 0 -> 519 bytes .../res/drawable-xhdpi/arrow_circle_down.png | Bin 0 -> 985 bytes .../res/drawable-xhdpi/arrow_circle_up.png | Bin 0 -> 970 bytes .../res/drawable-xxhdpi/arrow_circle_down.png | Bin 0 -> 1331 bytes .../res/drawable-xxhdpi/arrow_circle_up.png | Bin 0 -> 1321 bytes .../drawable-xxxhdpi/arrow_circle_down.png | Bin 0 -> 1729 bytes .../res/drawable-xxxhdpi/arrow_circle_up.png | Bin 0 -> 1710 bytes .../drawable/translation_row_background.xml | 6 + app/src/main/res/layout/translation_row.xml | 2 +- .../res/menu/downloaded_translation_menu.xml | 16 ++ app/src/main/res/values/strings.xml | 4 + .../BaseTranslationPresenterTest.kt | 10 +- 31 files changed, 386 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt create mode 100644 app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt create mode 100755 app/src/main/res/drawable-hdpi/arrow_circle_down.png create mode 100755 app/src/main/res/drawable-hdpi/arrow_circle_up.png create mode 100755 app/src/main/res/drawable-mdpi/arrow_circle_down.png create mode 100755 app/src/main/res/drawable-mdpi/arrow_circle_up.png create mode 100755 app/src/main/res/drawable-xhdpi/arrow_circle_down.png create mode 100755 app/src/main/res/drawable-xhdpi/arrow_circle_up.png create mode 100755 app/src/main/res/drawable-xxhdpi/arrow_circle_down.png create mode 100755 app/src/main/res/drawable-xxhdpi/arrow_circle_up.png create mode 100755 app/src/main/res/drawable-xxxhdpi/arrow_circle_down.png create mode 100755 app/src/main/res/drawable-xxxhdpi/arrow_circle_up.png create mode 100644 app/src/main/res/drawable/translation_row_background.xml create mode 100644 app/src/main/res/menu/downloaded_translation_menu.xml diff --git a/app/build.gradle b/app/build.gradle index 9a41c84eb7..90d9c9038f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { defaultConfig { minSdkVersion deps.android.build.minSdkVersion targetSdkVersion deps.android.build.targetSdkVersion - versionCode 3010 - versionName "3.0.1" + versionCode 3100 + versionName "3.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt new file mode 100644 index 0000000000..3d19b21020 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt @@ -0,0 +1,8 @@ +package com.quran.labs.androidquran.common + +class LocalTransationDiplaySort : Comparator { + override fun compare(p0: LocalTranslation?, p1: LocalTranslation?): Int { + if (p0 == null || p1 == null) return 0; + return p0.displayOrder.compareTo(p1.displayOrder); + } +} diff --git a/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslation.kt b/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslation.kt index 99c2e1366b..d541bf54f5 100644 --- a/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslation.kt +++ b/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslation.kt @@ -9,7 +9,8 @@ data class LocalTranslation( val url: String = "", val languageCode: String? = "", val version: Int = 1, - val minimumVersion: Int = 2) { + val minimumVersion: Int = 2, + val displayOrder: Int = -1) { fun getTranslatorName(): String { return when { diff --git a/app/src/main/java/com/quran/labs/androidquran/common/TranslationMetadata.kt b/app/src/main/java/com/quran/labs/androidquran/common/TranslationMetadata.kt index 48ea1cdd84..307a8d0893 100644 --- a/app/src/main/java/com/quran/labs/androidquran/common/TranslationMetadata.kt +++ b/app/src/main/java/com/quran/labs/androidquran/common/TranslationMetadata.kt @@ -5,4 +5,5 @@ import com.quran.data.model.SuraAyah data class TranslationMetadata(val sura: Int, val ayah: Int, val text: CharSequence, + val localTranslationId: Int? = null, val link: SuraAyah? = null) diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/translation/Translation.kt b/app/src/main/java/com/quran/labs/androidquran/dao/translation/Translation.kt index bb155f5755..1d01051d1a 100644 --- a/app/src/main/java/com/quran/labs/androidquran/dao/translation/Translation.kt +++ b/app/src/main/java/com/quran/labs/androidquran/dao/translation/Translation.kt @@ -14,7 +14,8 @@ data class Translation(val id: Int, val saveTo: String, val languageCode: String, val translator: String? = "", - @Json(name = "translatorForeign") val translatorNameLocalized: String? = "") { + @Json(name = "translatorForeign") val translatorNameLocalized: String? = "", + val displayOrder: Int = -1) { fun withSchema(schema: Int) = copy(minimumVersion = schema) } diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItem.kt b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItem.kt index dffe644e97..b5ce16c63a 100644 --- a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItem.kt +++ b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItem.kt @@ -1,7 +1,8 @@ package com.quran.labs.androidquran.dao.translation data class TranslationItem @JvmOverloads constructor(val translation: Translation, - val localVersion: Int = 0) : TranslationRowData { + val localVersion: Int = 0, + val displayOrder: Int = -1) : TranslationRowData { override fun isSeparator() = false @@ -16,4 +17,6 @@ data class TranslationItem @JvmOverloads constructor(val translation: Translatio fun withTranslationRemoved() = this.copy(localVersion = 0) fun withTranslationVersion(version: Int) = this.copy(localVersion = version) + + fun withDisplayOrder(newDisplayOrder: Int) = this.copy(displayOrder = newDisplayOrder) } diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt new file mode 100644 index 0000000000..723294a114 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt @@ -0,0 +1,8 @@ +package com.quran.labs.androidquran.dao.translation + +class TranslationItemDiplaySort : Comparator { + override fun compare(p0: TranslationItem?, p1: TranslationItem?): Int { + if (p0 == null || p1 == null) return 0; + return p0.displayOrder.compareTo(p1.displayOrder); + } +} diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java index dc298f0f9a..624fd013b1 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java @@ -82,10 +82,11 @@ public List getTranslations() { String languageCode = cursor.getString(6); int version = cursor.getInt(7); int minimumVersion = cursor.getInt(8); + int displayOrder = cursor.getInt(9); if (quranFileUtils.hasTranslation(context, filename)) { items.add(new LocalTranslation(id, filename, name, translator, - translatorForeign, url, languageCode, version, minimumVersion)); + translatorForeign, url, languageCode, version, minimumVersion, displayOrder)); } } cursor.close(); @@ -105,11 +106,31 @@ public void deleteTranslationByFile(String filename) { public boolean writeTranslationUpdates(List updates) { boolean result = true; db.beginTransaction(); + try { for (int i = 0, updatesSize = updates.size(); i < updatesSize; i++) { TranslationItem item = updates.get(i); if (item.exists()) { + int displayOrder = 0; + final Translation translation = item.getTranslation(); + + if (item.getDisplayOrder() > -1) { + displayOrder = item.getDisplayOrder(); + } else { + // get next highest display order + Cursor cursor = db.query( + TranslationsTable.TABLE_NAME, + new String[] {TranslationsTable.DISPLAY_ORDER}, + null, null, null, null, + TranslationsTable.DISPLAY_ORDER + " DESC", + "1" + ); + if (cursor != null && cursor.moveToNext()) { + displayOrder = cursor.getInt(0) + 1; + } + } + ContentValues values = new ContentValues(); values.put(TranslationsTable.ID, translation.getId()); values.put(TranslationsTable.NAME, translation.getDisplayName()); @@ -121,6 +142,7 @@ public boolean writeTranslationUpdates(List updates) { values.put(TranslationsTable.LANGUAGE_CODE, translation.getLanguageCode()); values.put(TranslationsTable.VERSION, item.getLocalVersion()); values.put(TranslationsTable.MINIMUM_REQUIRED_VERSION, translation.getMinimumVersion()); + values.put(TranslationsTable.DISPLAY_ORDER, displayOrder); db.replace(TranslationsTable.TABLE_NAME, null, values); } else { diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java index c4d612d9a2..5901a9644d 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java @@ -1,6 +1,8 @@ package com.quran.labs.androidquran.database; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -11,7 +13,7 @@ class TranslationsDBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "translations.db"; - private static final int DB_VERSION = 4; + private static final int DB_VERSION = 5; private static final String CREATE_TRANSLATIONS_TABLE = "CREATE TABLE " + TranslationsTable.TABLE_NAME + "(" + TranslationsTable.ID + " integer primary key, " + @@ -22,7 +24,9 @@ class TranslationsDBHelper extends SQLiteOpenHelper { TranslationsTable.URL + " varchar, " + TranslationsTable.LANGUAGE_CODE + " varchar, " + TranslationsTable.VERSION + " integer not null default 0," + - TranslationsTable.MINIMUM_REQUIRED_VERSION + " integer not null default 0);"; + TranslationsTable.MINIMUM_REQUIRED_VERSION + " integer not null default 0, " + + TranslationsTable.DISPLAY_ORDER + " integer not null default -1 " + + ");"; @Inject TranslationsDBHelper(Context context) { @@ -69,6 +73,34 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.endTransaction(); } } + if (oldVersion < 5) { + // Add display order column and add arbitrary order to existing translations + db.beginTransaction(); + try { + db.execSQL("ALTER TABLE " + + TranslationsTable.TABLE_NAME + + " ADD COLUMN " + + TranslationsTable.DISPLAY_ORDER + + " integer not null default -1" + ); + Cursor translations = db.query( + TranslationsTable.TABLE_NAME, new String[] { TranslationsTable.ID }, null, null, null, null, null + ); + for (int i = 0; i < translations.getCount(); i++) { + ContentValues values = new ContentValues(); + values.put(TranslationsTable.DISPLAY_ORDER, i); + db.update( + TranslationsTable.TABLE_NAME, + values, + TranslationsTable.ID + " = ?", + new String[] { String.valueOf(translations.getInt(0)) } + ); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } } static class TranslationsTable { @@ -82,5 +114,6 @@ static class TranslationsTable { static final String LANGUAGE_CODE = "languageCode"; static final String VERSION = "version"; static final String MINIMUM_REQUIRED_VERSION = "minimumRequiredVersion"; + static final String DISPLAY_ORDER = "userDisplayOrder"; } } diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenter.kt b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenter.kt index 1d990e626c..14e7cec422 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenter.kt +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenter.kt @@ -99,14 +99,16 @@ internal open class BaseTranslationPresenter internal constructor( if (element != null) { // replace with "" when a translation doesn't load to keep translations aligned val ayahTranslations = quranTexts.mapIndexed { index: Int, quranText: QuranText? -> - val translationMinVersion = translationInfo.getOrNull(index)?.minimumVersion ?: 0 + val translation = translationInfo.getOrNull(index); + val translationMinVersion = translation?.minimumVersion ?: 0 + val translationId = translation?.id ?: -1; val shouldProcess = translationMinVersion >= TranslationUtil.MINIMUM_PROCESSING_VERSION val text = quranText ?: QuranText(element.sura, element.ayah, "") if (shouldProcess) { - translationUtil.parseTranslationText(text) + translationUtil.parseTranslationText(text, translationId) } else { - TranslationMetadata(element.sura, element.ayah, text.text) + TranslationMetadata(element.sura, element.ayah, text.text, translationId) } } diff --git a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java index 648c4945eb..e489c83b82 100644 --- a/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java +++ b/app/src/main/java/com/quran/labs/androidquran/presenter/translation/TranslationManagerPresenter.java @@ -248,7 +248,7 @@ private List mergeWithServerTranslations(List serv override = new TranslationItem(translation.withSchema(versions.second), versions.first); } } else { - item = new TranslationItem(translation, local.getVersion()); + item = new TranslationItem(translation, local.getVersion(), local.getDisplayOrder()); } } else { item = new TranslationItem(translation); 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 b29518ec92..b4b1a1968d 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 @@ -37,6 +37,7 @@ import com.quran.labs.androidquran.QuranPreferenceActivity; import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.SearchActivity; +import com.quran.labs.androidquran.common.LocalTransationDiplaySort; import com.quran.labs.androidquran.common.LocalTranslation; import com.quran.labs.androidquran.common.audio.QariItem; import com.quran.labs.androidquran.di.component.activity.PagerActivityComponent; @@ -86,6 +87,8 @@ import com.quran.labs.androidquran.widgets.SlidingUpPanelLayout; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -1354,10 +1357,13 @@ private void requestTranslationsList() { .subscribeWith(new DisposableSingleObserver>() { @Override public void onSuccess(List translationList) { - int items = translationList.size(); + final List sortedTranslations = new ArrayList<>(translationList); + Collections.sort(sortedTranslations, new LocalTransationDiplaySort()); + + int items = sortedTranslations.size(); String[] titles = new String[items]; for (int i = 0; i < items; i++) { - LocalTranslation item = translationList.get(i); + LocalTranslation item = sortedTranslations.get(i); if (!TextUtils.isEmpty(item.getTranslatorForeign())) { titles[i] = item.getTranslatorForeign(); } else if (!TextUtils.isEmpty(item.getTranslator())) { @@ -1371,16 +1377,16 @@ public void onSuccess(List translationList) { if (currentActiveTranslations.isEmpty() && items > 0) { currentActiveTranslations = new HashSet<>(); for (int i = 0; i < items; i++) { - currentActiveTranslations.add(translationList.get(i).getFilename()); + currentActiveTranslations.add(sortedTranslations.get(i).getFilename()); } } activeTranslations = currentActiveTranslations; if (translationsSpinnerAdapter != null) { - translationsSpinnerAdapter.updateItems(titles, translationList, activeTranslations); + translationsSpinnerAdapter.updateItems(titles, sortedTranslations, activeTranslations); } translationItems = titles; - translations = translationList; + translations = sortedTranslations; if (showingTranslation) { // Since translation items have changed, need to diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java index ce23c5de96..c021391488 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java @@ -7,12 +7,14 @@ import android.os.Bundle; import android.util.SparseIntArray; import android.view.MenuItem; +import android.view.MotionEvent; import com.quran.labs.androidquran.QuranApplication; import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.dao.translation.Translation; import com.quran.labs.androidquran.dao.translation.TranslationHeader; import com.quran.labs.androidquran.dao.translation.TranslationItem; +import com.quran.labs.androidquran.dao.translation.TranslationItemDiplaySort; import com.quran.labs.androidquran.dao.translation.TranslationRowData; import com.quran.labs.androidquran.database.DatabaseHandler; import com.quran.labs.androidquran.presenter.translation.TranslationManagerPresenter; @@ -26,6 +28,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -57,6 +60,8 @@ public class TranslationManagerActivity extends QuranActionBarActivity private Disposable onClickDownloadDisposable; private Disposable onClickRemoveDisposable; + private Disposable onClickRankUpDisposable; + private Disposable onClickRankDownDisposable; @Inject TranslationManagerPresenter presenter; @Inject QuranFileUtils quranFileUtils; @@ -89,6 +94,8 @@ public void onCreate(Bundle savedInstanceState) { quranSettings = QuranSettings.getInstance(this); onClickDownloadDisposable = adapter.getOnClickDownloadSubject().subscribe(this::downloadItem); onClickRemoveDisposable = adapter.getOnClickRemoveSubject().subscribe(this::removeItem); + onClickRankUpDisposable = adapter.getOnClickRankUpSubject().subscribe(this::rankUpItem); + onClickRankDownDisposable = adapter.getOnClickRankDownSubject().subscribe(this::rankDownItem); translationSwipeRefresh.setOnRefreshListener(this::onRefresh); presenter.bind(this); @@ -112,6 +119,8 @@ protected void onDestroy() { presenter.unbind(this); onClickDownloadDisposable.dispose(); onClickRemoveDisposable.dispose(); + onClickRankUpDisposable.dispose(); + onClickRankDownDisposable.dispose(); super.onDestroy(); } @@ -140,8 +149,13 @@ public void handleDownloadSuccess() { } } + List sortedItems = sortedDownloadedItems(); + int lastDisplayOrder = sortedItems.get(sortedItems.size() - 1).getDisplayOrder(); TranslationItem updated = downloadingItem.withTranslationVersion( - downloadingItem.getTranslation().getCurrentVersion()); + downloadingItem.getTranslation().getCurrentVersion() + ).withDisplayOrder( + lastDisplayOrder + 1 + ); updateTranslationItem(updated); // update active translations and add this item to it @@ -228,9 +242,12 @@ private void generateListItems() { TranslationHeader hdr = new TranslationHeader(getString(R.string.downloaded_translations)); result.add(hdr); + Collections.sort(downloaded, new TranslationItemDiplaySort()); + boolean needsUpgrade = false; for (TranslationItem item : downloaded) { result.add(item); + Timber.i("aaa translation " + item.name () + " order " + item.getDisplayOrder ()); needsUpgrade = needsUpgrade || item.needsUpgrade(); } @@ -330,6 +347,81 @@ private void removeItem(final TranslationRowData translationRowData) { builder.show(); } + private List sortedDownloadedItems() { + final ArrayList result = new ArrayList<>(); + for (TranslationItem item : allItems) { + if (item.exists()) result.add(item); + } + Collections.sort(result, new TranslationItemDiplaySort()); + return result; + } + + private void rankDownItem(TranslationRowData targetRow) { + TranslationItem targetItem = (TranslationItem) targetRow; + List sortedDownloads = sortedDownloadedItems(); + if (sortedDownloads.indexOf(targetItem) + 1 < sortedDownloads.size()) { // ignore last item in list + TranslationItem updatedItem = targetItem.withDisplayOrder(targetItem.getDisplayOrder() + 1); + ArrayList toUpdate = new ArrayList<> ( ); + toUpdate.add(updatedItem); + TranslationItem swapItem = null; + for ( TranslationItem translationItem : sortedDownloads ) { + if (translationItem.getDisplayOrder() == updatedItem.getDisplayOrder()) { + swapItem = translationItem; + break; + } + } + if (swapItem != null) { // swap item order + if (swapItem.getDisplayOrder() > 0) { + toUpdate.add(swapItem.withDisplayOrder(swapItem.getDisplayOrder() - 1)); + } + } else { // shift item order for higher items + for (TranslationItem translationItem : sortedDownloads) { + if (translationItem.getDisplayOrder() > -1 && translationItem.getDisplayOrder() < updatedItem.getDisplayOrder()) { + toUpdate.add(translationItem.withDisplayOrder(translationItem.getDisplayOrder() + 1)); + } + } + } + for ( TranslationItem toUpdateItem : toUpdate ) { + updateTranslationItem(toUpdateItem); + } + generateListItems(); + } + } + + private void rankUpItem(TranslationRowData targetRow) { + TranslationItem targetItem = (TranslationItem) targetRow; + List sortedDownloads = sortedDownloadedItems(); + if (sortedDownloads.indexOf(targetItem) > 0) { // ignore first item in list + ArrayList toUpdate = new ArrayList<>(); + int updatedDisplayOrder = targetItem.getDisplayOrder(); + if (updatedDisplayOrder > 0) { + TranslationItem updatedItem = targetItem.withDisplayOrder(--updatedDisplayOrder); + toUpdate.add(updatedItem); + } + TranslationItem swapItem = null; + for (TranslationItem translationItem : sortedDownloads) { + if (translationItem.getDisplayOrder() == updatedDisplayOrder) { + swapItem = translationItem; + } + } + if (swapItem != null) { // swap item order + toUpdate.add(swapItem.withDisplayOrder(swapItem.getDisplayOrder() + 1)); + } else { // shift item order for lower items + for (TranslationItem translationItem : sortedDownloads) { + if (translationItem.getDisplayOrder() > updatedDisplayOrder) { + toUpdate.add(translationItem.withDisplayOrder(translationItem.getDisplayOrder() - 1)); + } + } + } + if (!toUpdate.isEmpty()) { + for (TranslationItem toUpdateItem : toUpdate) { + updateTranslationItem(toUpdateItem); + } + generateListItems(); + } + } + } + private boolean removeTranslation(String fileName) { String path = quranFileUtils.getQuranDatabaseDirectory(TranslationManagerActivity.this); if (path != null) { diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java index 7c44c099e4..3cdd039fb0 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java @@ -1,8 +1,10 @@ package com.quran.labs.androidquran.ui.adapter; -import android.content.Context; import android.text.TextUtils; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -18,6 +20,8 @@ import java.util.List; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.view.ActionMode; import androidx.recyclerview.widget.RecyclerView; import io.reactivex.Observable; import io.reactivex.subjects.UnicastSubject; @@ -26,12 +30,23 @@ public class TranslationsAdapter extends RecyclerView.Adapter onClickDownloadSubject = UnicastSubject.create(); private final UnicastSubject onClickRemoveSubject = UnicastSubject.create(); + private final UnicastSubject onClickRankUpSubject = UnicastSubject.create(); + private final UnicastSubject onClickRankDownSubject = UnicastSubject.create(); + + private final AppCompatActivity activity; + private final TranslationSelectionListener selectionListener; + private final DownloadedItemActionListener downloadedItemActionListener; private List translations = new ArrayList<>(); - private Context context; - public TranslationsAdapter(Context context) { - this.context = context; + private ActionMode actionMode; + + private TranslationItem selectedItem; + + public TranslationsAdapter(AppCompatActivity anActivity) { + this.activity = anActivity; + this.selectionListener = new TranslationSelectionListener(this); + this.downloadedItemActionListener = new DownloadedItemActionListener(); } @NotNull @@ -47,6 +62,8 @@ public void onBindViewHolder(@NotNull TranslationViewHolder holder, int position switch (holder.getItemViewType()) { case R.layout.translation_row: TranslationItem item = (TranslationItem) rowItem; + holder.setItem(item); + holder.itemView.setActivated(item.equals(selectedItem)); holder.getTranslationTitle().setText(item.name()); if (TextUtils.isEmpty(item.getTranslation().getTranslatorNameLocalized())) { holder.getTranslationInfo().setText(item.getTranslation().getTranslator()); @@ -58,6 +75,8 @@ public void onBindViewHolder(@NotNull TranslationViewHolder holder, int position ImageView rightImage = holder.getRightImage(); if (item.exists()) { + rightImage.setVisibility(View.GONE); + holder.itemView.setOnLongClickListener(holder.actionMenuListener); if (item.needsUpgrade()) { leftImage.setImageResource(R.drawable.ic_download); leftImage.setVisibility(View.VISIBLE); @@ -65,10 +84,6 @@ public void onBindViewHolder(@NotNull TranslationViewHolder holder, int position } else { leftImage.setVisibility(View.GONE); } - rightImage.setImageResource(R.drawable.ic_cancel); - rightImage.setOnClickListener(holder.removeListener); - rightImage.setVisibility(View.VISIBLE); - rightImage.setContentDescription(context.getString(R.string.remove_button)); } else { leftImage.setVisibility(View.GONE); rightImage.setImageResource(R.drawable.ic_download); @@ -80,6 +95,7 @@ public void onBindViewHolder(@NotNull TranslationViewHolder holder, int position } break; case R.layout.translation_sep: + holder.itemView.setActivated(false); holder.getSeparatorText().setText(rowItem.name()); break; } @@ -104,6 +120,14 @@ public Observable getOnClickRemoveSubject() { return onClickRemoveSubject.hide(); } + public Observable getOnClickRankUpSubject() { + return onClickRankUpSubject.hide(); + } + + public Observable getOnClickRankDownSubject() { + return onClickRankDownSubject.hide(); + } + public void setTranslations(List data) { this.translations = data; } @@ -112,6 +136,44 @@ public List getTranslations() { return translations; } + public void setSelectedItem ( TranslationItem selectedItem ) { + this.selectedItem = selectedItem; + notifyDataSetChanged(); + } + + class TranslationSelectionListener { + private final TranslationsAdapter adapter; + + TranslationSelectionListener(TranslationsAdapter anAdapter) { + adapter = anAdapter; + } + + void handleSelection(TranslationItem item) { + adapter.setSelectedItem(item); + } + + void clearSelection() { + adapter.setSelectedItem(null); + } + } + + class DownloadedItemActionListener { + void handleDeleteItemAction() { + onClickRemoveSubject.onNext(selectedItem); + selectedItem = null; + } + + void handleRankUpItemAction() { + onClickRankUpSubject.onNext(selectedItem); + selectedItem = null; + } + + void handleRankDownItemAction() { + onClickRankDownSubject.onNext(selectedItem); + selectedItem = null; + } + } + class TranslationViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { @Nullable TextView translationTitle; @@ -119,6 +181,7 @@ class TranslationViewHolder extends RecyclerView.ViewHolder implements View.OnCl @Nullable ImageView leftImage; @Nullable ImageView rightImage; @Nullable TextView separatorText; + @Nullable TranslationItem item; TranslationViewHolder(View itemView, int viewType) { super(itemView); @@ -132,6 +195,10 @@ class TranslationViewHolder extends RecyclerView.ViewHolder implements View.OnCl } } + void setItem ( @Nullable TranslationItem item ) { + this.item = item; + } + TextView getSeparatorText() { return separatorText; } @@ -152,19 +219,75 @@ ImageView getRightImage() { return rightImage; } - final View.OnClickListener removeListener = v -> { - TranslationItem item = (TranslationItem) translations.get(getAdapterPosition()); - onClickRemoveSubject.onNext(item); + final View.OnLongClickListener actionMenuListener = v -> { + if (actionMode != null) { + actionMode.finish(); + selectionListener.clearSelection(); + } else if (activity != null) { + selectionListener.handleSelection(item); + actionMode = activity.startSupportActionMode(new ModeCallback()); + } + return true; }; @Override public void onClick(View v) { - TranslationItem item = (TranslationItem) translations.get(getAdapterPosition()); - if (item.exists() && !item.needsUpgrade()) { - onClickRemoveSubject.onNext(item); - } else { + if (actionMode != null) { + actionMode.finish(); + } + selectionListener.clearSelection(); + if (!item.exists() || item.needsUpgrade()) { onClickDownloadSubject.onNext(item); } } } + + private class ModeCallback implements ActionMode.Callback { + @Override + public boolean onCreateActionMode ( ActionMode mode, Menu menu ) + { + MenuInflater inflater = activity.getMenuInflater(); + inflater.inflate(R.menu.downloaded_translation_menu, menu); + return true; + } + + @Override + public boolean onPrepareActionMode ( ActionMode mode, Menu menu ) + { + return false; + } + + @Override + public boolean onActionItemClicked ( ActionMode mode, MenuItem item ) + { + switch(item.getItemId ()) { + case R.id.dtm_delete: + downloadedItemActionListener.handleDeleteItemAction(); + endAction(); + break; + case R.id.dtm_move_up: + downloadedItemActionListener.handleRankUpItemAction(); + endAction(); + break; + case R.id.dtm_move_down: + downloadedItemActionListener.handleRankDownItemAction(); + endAction(); + break; + } + return false; + } + + @Override + public void onDestroyActionMode ( ActionMode mode ) + { + if (mode == actionMode) actionMode = null; + } + + private void endAction() { + if (actionMode != null) { + selectionListener.clearSelection(); + actionMode.finish(); + } + } + } } diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java b/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java index bc86d38e38..67377da203 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java @@ -9,6 +9,7 @@ import com.quran.labs.androidquran.BuildConfig; import com.quran.labs.androidquran.R; +import com.quran.labs.androidquran.common.LocalTransationDiplaySort; import com.quran.labs.androidquran.common.LocalTranslation; import com.quran.labs.androidquran.common.QuranAyahInfo; import com.quran.labs.androidquran.common.TranslationMetadata; @@ -19,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import androidx.annotation.NonNull; @@ -107,22 +109,23 @@ public void setVerses(@NonNull QuranDisplayData quranDisplayData, rows.add(new TranslationViewRow(TranslationViewRow.Type.QURAN_TEXT, verse)); } - // added this to guard against a crash that happened when verse.texts was empty - int verseTexts = verse.texts.size(); - for (int j = 0; j < translations.length; j++) { - final TranslationMetadata metadata = verseTexts > j ? verse.texts.get(j) : null; + final LocalTranslation[] sortedTranslations = Arrays.copyOf(this.translations, this.translations.length); + Arrays.sort(sortedTranslations, new LocalTransationDiplaySort()); + + for (int j = 0; j < sortedTranslations.length; j++) { + final TranslationMetadata metadata = findText(verse.texts, sortedTranslations[j].getId()); CharSequence text = metadata != null ? metadata.getText() : ""; if (!TextUtils.isEmpty(text)) { if (wantTranslationHeaders) { rows.add( new TranslationViewRow(TranslationViewRow.Type.TRANSLATOR, verse, - translations[j].getTranslatorName())); + sortedTranslations[j].getTranslatorName())); } rows.add(new TranslationViewRow( TranslationViewRow.Type.TRANSLATION_TEXT, verse, text, j, metadata == null ? null : metadata.getLink(), - "ar".equals(translations[j].getLanguageCode()))); + "ar".equals(sortedTranslations[j].getLanguageCode()))); } } @@ -179,6 +182,15 @@ public void onClick(View v) { } } + private TranslationMetadata findText(List texts, Integer translationId) { + for (TranslationMetadata text : texts) { + if (translationId.equals(text.getLocalTranslationId())) { + return text; + } + } + return null; + } + /** * This method updates the toolbar position when an ayah is selected * This method is called from the onScroll listener, and as thus must make sure not to ask diff --git a/app/src/main/java/com/quran/labs/androidquran/util/TranslationUtil.kt b/app/src/main/java/com/quran/labs/androidquran/util/TranslationUtil.kt index 90798b8a58..9ee51ac8ec 100644 --- a/app/src/main/java/com/quran/labs/androidquran/util/TranslationUtil.kt +++ b/app/src/main/java/com/quran/labs/androidquran/util/TranslationUtil.kt @@ -14,7 +14,7 @@ open class TranslationUtil(@ColorInt private val color: Int, private val quranInfo: QuranInfo ) { - open fun parseTranslationText(quranText: QuranText): TranslationMetadata { + open fun parseTranslationText(quranText: QuranText, translationId: Int): TranslationMetadata { val text = quranText.text val hyperlinkId = getHyperlinkAyahId(quranText) @@ -39,7 +39,7 @@ open class TranslationUtil(@ColorInt private val color: Int, val range = it.range spannable.setSpan(span, range.start, range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } - return TranslationMetadata(quranText.sura, quranText.ayah, spannable, suraAyah) + return TranslationMetadata(quranText.sura, quranText.ayah, spannable, translationId, suraAyah) } companion object { diff --git a/app/src/main/res/drawable-hdpi/arrow_circle_down.png b/app/src/main/res/drawable-hdpi/arrow_circle_down.png new file mode 100755 index 0000000000000000000000000000000000000000..15ac3d534035747c0163d03cbc070e3e9045994c GIT binary patch literal 763 zcmV0o96}g|0fu210KzZ~00BY>Ll^=KA%quU z7(xh#-Y)__yWI7E?sn0a58$5Lf4jT8s~vBrw%ct50b1zpG*LzQmc=v^53@ zQ7Ym5RP}lfDTw`=_p7O6BVv4x_p_pL72P0@J_&Hpt8ZpDYx;U@22+bUCEnR34e9NL=&>p;; zi#g|phb|UEn|S%97nB9G#vzN%q16t&_i5J$-U_twA(wY;;8C7~X?hc<2&FU&JV9#| zV6YHcX!*@7Ju1LpC2aIaO7pU)y}KUFu-=D0C?wsm2CZG7!S11zlfDgWwE_)x1Ff25 z!ydGY0u9!H)=aYD^H%bxK!aUE>$)FIAA4-|6YS>#S}(~Dcn7VL!*Kl+A0TP1BpY_1 z1%?M!!%+60&;A>c;p{`%k0{-+V%_=P;q2=UWsOamjc2m)!u}rRWTJi$+vfwSWHG&+6Y9WGFw=O(|wnQa|p?s{N4P1SX{2en~kl|2Gtf0+Ma{0~W2HG08 z84UG+2-**>T<0ILI@!Q7=~d02*WS{1PCDvVF(aH2rt4g zgb)tB-2V?A@(QGk9 z3)wUls^b%u1elvGcS7eeZb&o}ab7zDu8Ytngi$YwbstE8?=BPN>#{*VJj)H>gQXV$Y1bB43uM7r9 ze!0BMiDC)O7PS};`!)h-rpUP1!5EqiyaXnya!9#X7w45GKsCWjOBV`e98bX z(D*4R)(D#sEw@!~DJV99CU@!}Yv|hN-f1!WIG^WyJ3VL588q&mztmkmXK4OHsYht0 zmI0g4Y~d$Zs)hiXl6Bx2G;8D&DCJ=b&Cu0*6B>$K)L);mgCkn%LZUxtw+Ydc^xeR((!pH))B?YA;wIYG!zXcS(N` qE1`ido)0>xqlnziHP>8ok@*LCx2<6UY7D3V0000aLsD-uzji9!|yTx(p*g~yudXNs( zLby*{?KjLJUE!H*P9ZHFZAL?FA;mT~@e#EWpVm4uNWCIwq2HiCfxvM6{?XzVqyyXv z2CCr<=}WhG0%`0}$pgur8Kj|Z;TDot;XqwTbKSxOQoX`~JV4iXAAO2mAo+!dLcz5INQvng@8FjBa(zb%X;5@i25DO2kA#)cyE1=Sjn2gU z0q{)^5J7DU*J6+wmQV|9zu~Q+wllcVUG#7SXb<-Q#R>8AJa{QC`0(oB7NjGb zgu_T)!rga+RI?pCfizMMqZw)rX<$2W2gy|pqZlgs@st*}1J97Es$mpEMZf6M#xl@v z>R}W^MUNTjq!>uOYkbA_m(I2WLr9ez2Wmpv+71jLHF6xN18HeHa0bcGejwf7kry#3 z$YCIPt+_y|zxoxuLu%(RkY_CDmnV!N?crqja4j1f$4h$B71A`re-hTZXQgplRac_B z^X76;?F4CRzas0pTp^yH_6=`?czd)JUr`SykmX^j9BKx$06rS17>@)OBOGBi%wm(s znz|xwA-aVOlifGQ9Z9>wN3ozvzewAdAwUS}j2$Lu$snKpbU#;?`brKho3;P|002ov JPDHLkV1l%c-829I literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/arrow_circle_down.png b/app/src/main/res/drawable-xhdpi/arrow_circle_down.png new file mode 100755 index 0000000000000000000000000000000000000000..fd6a27c9a6c3abb40bdb41d756ed983aa92b7b3e GIT binary patch literal 985 zcmV;~119{5P)c483MhtPD28ANieeawVhDyID2f7xAqaw^ zI0T2!AAosxOK~9OHxc?q}+XzDp>4ds@z2u6M86w z&CzXWqnrzSirTy3R#<_(NpoVli0=8^su3 z6sZd@CDIqR9<#rJcf#iAY*0XZSO}|M@#7}Ntyv?WluN~@`VA?$u;UF3=v3IR`fWv6 zj_nN$XivEpbuKta-%=zDC;$^-Cv`4%CG2d21G*IUA!dQ~dV>RcAFbfUy|BX#4(P40 zn>rVp3ESJ?fR2Ps(tqjA2xwo}bZuW6L+K17z3OLm0-6i!HYB8B#L2{6oq!&M1ZKKx z2x%C3K6D$icj;#}VVE|YYB&=%ia8=U&He(0>B9-bM`44Qt;SXMmoQ8oP5{0OJBe9T zJz^_$ND0E(MG>v|MJLISsXC;pa8eEbX32(32|`K`PQu|IPK8ael`5nJ;p`+C{-LYf zc@`0d;c$}Pr8%}z4VfOX6Z?lG2&acdvf&^4P2A!Zp`I)7GK&E}ge}l(a5zWGUB^9J zQSK)?4c*K>7q*B#*TPrj#@K35I2}AFcbUb89*XoIn*7gvN_K1kC#qep^G?Dxz9?7V zI706U5An$IXyo2>lN$BQLG zf=8T3?%g^VAopi?=&tj8*w}KW_=KZ6VPPA`7-4QX*Vt~tzT~0jdBhb?(Z>P0zZH3h zc#X3f$2tpKWKDt-6xJ_CKWifN@YQ-07@?ar9S-rs>fEB2Z5s43kw$?rUS*pO-5SR! ztB-ZEll_a(!CMS)jWHfD2fz&X_=YPC(0|S=yVYv7TCLXqy>xyRG%&*700000NkvXX Hu0mjfma)8W literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/arrow_circle_up.png b/app/src/main/res/drawable-xhdpi/arrow_circle_up.png new file mode 100755 index 0000000000000000000000000000000000000000..a4a1ee111a7ba29bde1e4d13027c74b26b2632cb GIT binary patch literal 970 zcmV;*12z1KP)a z`1}EQ&fVPCJ=EsU#}W3ijcpv@7(Z}@zI-P5uPkq;yBvS?BTlgSkI%I;^m5$7t!2ik zqaXVrTEI)7i`t^&tmDOe9W;_iu!|@2br&6|W46&EhdCT!Qi|IRmu7pz){=(Off=Gd zxY0x^+XHIJl3)|hvQ4lX^PxPE?Y>0ozFComQHDvhWqU2t1dB}!WV=j?ga%S!Bh*W} zP@V|;D0JIwwt5~*q7}#DLFg|n-;QFf}XGm>Jip-7ZHZZR9L%+?Tb`c zD?%I)AyP-!IQa~m2pgdmB~FA$*uqrMCnkP83TsD>6C+Yj*!5=?q!VF>QR762gfn4( zKC`R{nP5F?oCuMyCEHGt7h1yZsW0p_$qRSF&f~|46A4$seik`keUBd}P9*#& zY~aPKu)X+k;zYu+u*W1X425mQj}s>n4utiSykv+UCtf6M3mYsgP?@fUEn&kXFN}oM zS2)nVu$Lq+yb0S~;Xp^ix<#&CnkyXWTv)rvErPQZ4)jY{tH`Cs-3kZ#E$p<&p=yk^ zRSSeoq=gN?s72XiT3(FOyvv|fAbG#!UkAdl|a~tviXO)Y!@pO=tkHCYq2(& z#@Hx(AT%(IcAfyuG9Ga&V4h3yEhz$?g-y{YahwC$I>mllk?kF|k{)Is37Zyv$O{j$ z^{`f=I5oVUlTY6fJB-{?*(fHCun-w?208ZoV-t3SPU_(89!gFQ_l` z`>-CEZ-8qYkUtjIafmiXV7?C4%Wy9FMm}TQ;S5deqW;&Aw}~LI*v( sVFZ97Uh#xGw9w4+%C1x@l}e@ZKgVZ#6%dMRlK=n!07*qoM6N<$f||p^lK=n! literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/arrow_circle_down.png b/app/src/main/res/drawable-xxhdpi/arrow_circle_down.png new file mode 100755 index 0000000000000000000000000000000000000000..b41fb39a0538c7911a680ee2e9fd44f664d245ac GIT binary patch literal 1331 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz3?z5#SpFYK?F#S-aRt)FbZ*KfHQ!wX=>0$Lz=dZ}&T}2t+Uc{Cd7WPx|iP-_P$?k+FDwU1Pg~ zO>lPe`*xGd`J0QqlTR;p^6@&vz`*><)5S5Qg7NLm>rIQiL|PxF`X$FHZQHhBfkN52 zkALM~AJIyV4q+@dt(z|Uhf~_q;`ijabDw@{Oy5G}m^NzBkr?9NuR6}{K)Y%CKvulH@|;ymZc zwP$5jrK5J)zI)*~`RLkC=3L|c==B#qom?9w#}+mH&Ihgw1+P~g^Q!NVUoY@l?UCEz zHpT7ddcSfMYTb#rW$&P2sy6QrB@ggEpxD;ck>nxdAga_N&s@4_bmpX?@F=#j3} zxWVk@t8#7emMhUK^i{6E4c2;~V!p`4h0E28TY9;P=H7=(gj$9CHi}QtD>&8EG~HkH zF#A$2KMoDwiF$3StoM(ekJJ2-cRlNban0h<*X(KU6khtz zTzWr9e^1d?RqyRfH%7~F6>DVO(&Y(Z-oEI;&aPQyue-XOIoHY8pUlbJeqqUl@cD0# zU053_RX)}B>)%UDRI=i-7* z<$}x?g}5ZCdU2msm-wgk*X>C_Ik$_V&`E!dvtPwupLS*1DfM-E&dFZGGdD}~e0;Yt z>3$AtcKdzs;k6sB6VFX*6ApWHEu4e-E#vmC3r5evwlr<)=iEMT!STP3rp31`TXuY{ zx%8|<9oEk(GbHK?kKbzDcC}^qGRCAQ_jhP>1?`Fcx>iu+(8}qrbhCdPwe8>QByL~y zeqZgoopJ{!SU+n#`Qy;h#&geSpJ$%NTdXSg@!8X$jT6o@KKy(_3QRC9(~JX|7=%1u=rqL@O1Ta JS?83{1OU*t)JOmT literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/arrow_circle_up.png b/app/src/main/res/drawable-xxhdpi/arrow_circle_up.png new file mode 100755 index 0000000000000000000000000000000000000000..1378ef5243368cac8da59e32f6f057572a27497f GIT binary patch literal 1321 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz3?z5#SpFYK?F#S-aRt)FbZ*KfHQ!wX=>0$Lz=dZ}&T}2t+Uc{Cd7WPx|iP-_P$?k+FDwU1Pg~ zO>lPe`*xGd`J0QqlTR;p^6@&vz`*>#)5S5Qg7MAG)@iRiL|PwS7A*O|%aSSdsHN=s z_xd##SGDr0hkCx-bo+kMoBlb6ob2oV{Foanu`KoatKPKDSN|BQGV!DLQ@K0`(bZ zRxI#k%e}3wb%d)hsb=q{>+haDd-mL}n~`V3u&qE4f2{y~cH4 zHjmtPlS*BOTjwfxSm=(~csnGj7#G!~&)1%{h!*Q2~{6MWL97Yq_xiog zJ4${u^4cyHi}o%O3zR)HJE?o^-VN{nHmkPyUlj|lZZLSpzT*6r8MDrvu64-B+%n@9 zhg1LWdB#^V3{B;oru*F#$mtaej&|Iz!+)VakX8M_PV&A&O$Jb*ZHxVjE8;EbK+)aJ z7oIIsznWpVH9BvX;!B_;Z_6^bp1DzOT!}8rjX1=w9x)S;x>L@yc-D;5wZ=Qz62gR| zomYI+-dEF8AM;cEq4~V~Z#NnK+p9?UvR1SqaFcrzU$&FweF1n7nDDRZGIO-F%0a z#Jmyv`Ch<(LfF$J!zHG3FW0xE1aRst`RI^%^YHRVb96)Q^{`2*baQQ1bUm_Bp>_5r zcERAQ$`h7`bZl&SvO53Tba&PmlZAGvh6=hK^Di?knR+hpfXn`?ZPzLbHg5jl;cegc ztmWp7pI)BM`8+Sy&1Egy#H;x`BI$YOm5TTqoVr0*I5&KLHer#;VY&MXte5rQ+^kxX zv-!xrP$$bC^Eam_30)6!{->3@i_0rE)pvivgTf5Hb8{Z;jqd5ksi*PoA>{LvPIH9?B+gl=t$);iI#aAlqI(Vfv589pmT zj-OhP@g^v<`JA#yN_P9I3-waU?pHppFxu)4Q%Nk3V#KM|O3o uZ0dm*uX8i%>umU69Pht>FJ2m0R;Wp`J+|Zf_YPQUFnGH9xvX)P^GM>h04v8kYJjG#5Om0&yQPC#rxJ(X*ky&Mw z>oIf7B@>BdZgFx(#Ac;3w`zKHcFuosKF{;M-{*OMdB5LZzS&-$r&YkZU;qH1;)cVX zlWYFJqog3;f)56gLdc5BGFd#an`nVmWa;$@NPQG@c1cX_Jw;-h=(o) z0swnRZdi;@67b{fwA1?lP09jBV}EubW+uWAh?!XxFrLYF&nB8RtJ9*Yl62?mKI-@? zA8Gqq(b*>F1tH;E-+5_=4@y`VV={2jIEZm!KtdPkJwZ7t;m^w7@SAM3u+xE6}A zZ?)-JCa1!_-()*gyaA2o;iely@covqxY3nX=ces8DtU631IuQdprfsVLJ0Sc6cl~1 zDq4rQMe<>R&z(Z)DS7frSKpEFDPw81j)rAeeRBU+b5;ytp9W&AMQwO)xpLQpgEmcy5F@Es zMo`mqOmVV!y*HflB3zYcQ?9G%N-^TuAoO)RAaSn^l)~ys)Kyb#h|q#yfmMvLr;^WL zrLXd-*|=IW=SymsTbQeRy-Um?L(Zg^%&V%FONT(VR!U(E$V?ZqsAL#$O^NQ24Qw^& zKfY@9b|3>=0!+|o@~>op6&;uMWnK4YU>NQB=5_0o9!T_|$Ezsy0%EUbsjJy}cS8~7 zaW?F-+f%3bkt>DEmhZg0ZRmPYrzwG%xgf2N+Ui^3cuOj1v}zHi+{t1&^nIcEkfLyY zRc@49G5mgBE?80+hX^y5^m{l)EWM=m7tu}@K>H49DU8|sQqi~$%dptFQd$}clf-0Y zZ5VAP(y!-l<|X+oO@L7Xv7fCNns(Ksj%lg|wI%8y#=SGVSf3F@PW8$|9fDxmCN3Zk zY~IC0X`Sb%)#hzxjO2*Yxs@^HA!?r_@CemPQYi*6`z2*(0CXATzxh{6sLW`J+50wP zp>TdPaYCh3@a3WPkgfzIjO6beOQVo8Q30uhF(ol$>#oV@Wd5Z5Wmbpy82OfvX99s6c zu+`$n%nrg`AG*(N-&?DqmS^A2u^FF*Z~JWJH(G1GQq%3Ojy-A^x#A^-uRrPij4QAc zbb)GR4A`<)YcfKJsW zP-ZC}%jFEsJgM^!FPmpsG#rS}GY;5CeHutlKU;#Fw#Kec67I}eA$?M>SoQPuSRMRE zp{9Cx`f#HNK~E$h4%9O192bCMq?8>?LvU7yS>m`5G@HE~dKYRqg5tnRW-aMYN7#H^S)Y<*t=pJ*;uvnU+oO_|MP5e1BWEx1 z^Bp&){JeMs2VUM#?hZO?ZIB0r&k4E7liZo8h0pZli@L)2nn|PfE{P4eOO}4;?tlDf a=>hm^b!sbe*B;4B9N^~SiLG}E&iDu6O`To< literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/arrow_circle_up.png b/app/src/main/res/drawable-xxxhdpi/arrow_circle_up.png new file mode 100755 index 0000000000000000000000000000000000000000..22433dc5e1c4ba7056ce201437e46c6d0866f684 GIT binary patch literal 1710 zcmdUv`#Tc~7{^sJYng4Gx!Vb8wj%dSvS~65jk#qxO04OElkB8}Lfb4%=GvB1Tc%uc z$t7vs^e7FbAu-M&k?Um2ts7^Z|Kfa~=Y2oV^L^i6zQ24k{74=UupU@NMFm3ibn{n| z`*T2>lv~Q%nyEyqpHCn`$@Txao-)k^${2XMKPf;($I|0S%9Yv|Bb`qk2y#yC2YDB- ze+D2z=S2bPhQEJXX^1p?-I`zk%Swu$?DciLHx%W}m>uh9o7nC{mmSQ%9fb@FRZ&r; z5Z&;B=Qhj@^+#0tAks%!$fPgNbl}qCr}dK{w}#h)ez0xAWew={SX>$AaS??9rmy5Y z_^Dq1X&?J57E|KGW`(=@4uy%1ly*mJ(tnfg4xR=6cvcb5XEJ75q@+G>Xm)X)$HIxIjv5&EDywtdHH`+OA@s|p@fwsC&=D2O^{ce5CbamXvDKg-ZeOJ{s z&q8wX=*tV&oN#$E+HRVDdhw<7}>k*dlQsHWS7# zzYDO<5CH^*_*{Nk3D_$6lbTvel{t}LH<1&(_Rb>LdW-b~l2v<46h0GWBe6#mACyeE zm*?_Ct2zwJF><^iMi;UDGpM}^zw;26z87&jK|c?JQAB%Q$I$k8ig$jthqm9qTlEfd z12xkL&0(^7S0usleAoVt5S~`JRXTWbM_g)Y#jxmbAcOicvCZpYdn`-FvlYjvwP$p< zqG#~dr9%@iAm>+i_^iA0+SUfcx`8dPtfq^!L1)>0t!OWNk0`Va=EeDyma8$E8#}cx zqGDN*RS6RV!yK5;A{Q;QX1H%OSL)y*b|_V@xzd1TB1P=5#d7>BC z9@U;R^eJrOp~~1t0vU>%i#;xJ$}<);LWh{_R+^1LdVSnGO6xo*u~~R-y@PdHc!~yV zq%w-Ua-<;FoiZV-C#?(Y>Le3Fm|Ihl+qM-1fje9!DE+`RKXRf$d^@nZ0JXHy6(T${z+L ze5LFqSP_SeeBErKxYK&i-#^+SlNcV;U*$kDpN@kBwiPxLiV8ovyDo0md&P2^F~ivU zpn{Tc>1J$eR2A7|7>=7WjTmh4^(6zAu*q`TfG(<08!^}hH1-V@F;g+D$Z?p$^C8FW6RzOo7pXbrSI3!&R9a-!o;PjQZk40Ni4r8F`rJ!zk2y}@9J~W zIzMz*LR`0zi(FcJ<55f{Bd;c5gFz-E`0Rl5{YBsZ%JIQB=#5*CwJ719Bg)2BAreS# JHLl?o{{`9Sey0Ec literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/translation_row_background.xml b/app/src/main/res/drawable/translation_row_background.xml new file mode 100644 index 0000000000..a9ee399f8d --- /dev/null +++ b/app/src/main/res/drawable/translation_row_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/translation_row.xml b/app/src/main/res/layout/translation_row.xml index b2272cf409..2877f06e5a 100644 --- a/app/src/main/res/layout/translation_row.xml +++ b/app/src/main/res/layout/translation_row.xml @@ -8,7 +8,7 @@ android:paddingTop="4dp" android:paddingBottom="4dp" android:orientation="horizontal" - android:background="?attr/selectableItemBackground" + android:background="@drawable/translation_row_background" > + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 03f0b13134..ca3bbbef7a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -237,6 +237,10 @@ Error exporting data Data exported to %1$s + Move Up + Move Down + Remove Translation + quranandroid+logs@gmail.com Warning Due to Android limitations, if you choose to place Quran diff --git a/app/src/test/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenterTest.kt b/app/src/test/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenterTest.kt index 95d7845d18..bd226b55e2 100644 --- a/app/src/test/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenterTest.kt +++ b/app/src/test/java/com/quran/labs/androidquran/presenter/translation/BaseTranslationPresenterTest.kt @@ -33,8 +33,8 @@ class BaseTranslationPresenterTest { MadaniDataSource() ) ) { - override fun parseTranslationText(quranText: QuranText): TranslationMetadata { - return TranslationMetadata(quranText.sura, quranText.ayah, quranText.text) + override fun parseTranslationText(quranText: QuranText, translationId: Int): TranslationMetadata { + return TranslationMetadata(quranText.sura, quranText.ayah, quranText.text, translationId) } }, QuranInfo(MadaniDataSource()) @@ -48,7 +48,7 @@ class BaseTranslationPresenterTest { init { put("one.db", LocalTranslation(1, "one.db", "One", "First", null, "", null, 1, 2)) put("two.db", LocalTranslation(2, "two.db", "Two", "Second", null, "", null, 1, 2)) - put("three.db", LocalTranslation(2, "three.db", "Three", "Third", null, "", null, 1, 2)) + put("three.db", LocalTranslation(3, "three.db", "Three", "Third", null, "", null, 1, 2)) } } @@ -74,7 +74,8 @@ class BaseTranslationPresenterTest { val verseRange = VerseRange(1, 1, 1, 1, 1) val arabic = listOf(QuranText(1, 1, "first ayah")) val info = presenter.combineAyahData(verseRange, arabic, - listOf(listOf(QuranText(1, 1, "translation"))), emptyArray()) + listOf(listOf(QuranText(1, 1, "translation"))), + arrayOf(LocalTranslation(1, "one.db", "One", "First", null, "", null, 1, 2))) assertThat(info).hasSize(1) val first = info[0] @@ -83,6 +84,7 @@ class BaseTranslationPresenterTest { assertThat(first.texts).hasSize(1) assertThat(first.arabicText).isEqualTo("first ayah") assertThat(first.texts[0].text).isEqualTo("translation") + assertThat(first.texts[0].localTranslationId).isEqualTo(1) } @Test From b8ac99ea08874409a515136a6a585af0883a12b9 Mon Sep 17 00:00:00 2001 From: ismailjones Date: Thu, 16 Jul 2020 09:18:46 -0400 Subject: [PATCH 2/5] cleanup --- app/build.gradle | 4 +- .../database/TranslationsDBAdapter.java | 1 + .../database/TranslationsDBHelper.java | 2 + .../ui/TranslationManagerActivity.java | 100 ++++++++++++++++- .../adapter/DownloadedItemActionListener.java | 7 ++ .../adapter/DownloadedMenuActionListener.java | 8 ++ .../ui/adapter/TranslationsAdapter.java | 103 ++---------------- .../res/menu/downloaded_translation_menu.xml | 6 +- app/src/main/res/values/strings.xml | 6 +- 9 files changed, 131 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedItemActionListener.java create mode 100644 app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedMenuActionListener.java diff --git a/app/build.gradle b/app/build.gradle index 90d9c9038f..9a41c84eb7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { defaultConfig { minSdkVersion deps.android.build.minSdkVersion targetSdkVersion deps.android.build.targetSdkVersion - versionCode 3100 - versionName "3.1.0" + versionCode 3010 + versionName "3.0.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java index 624fd013b1..2fa38c957e 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java @@ -129,6 +129,7 @@ public boolean writeTranslationUpdates(List updates) { if (cursor != null && cursor.moveToNext()) { displayOrder = cursor.getInt(0) + 1; } + cursor.close(); } ContentValues values = new ContentValues(); diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java index 5901a9644d..7d9fa03d3d 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java @@ -95,7 +95,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { TranslationsTable.ID + " = ?", new String[] { String.valueOf(translations.getInt(0)) } ); + translations.moveToNext(); } + translations.close(); db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java index c021391488..71dcde8332 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java @@ -6,8 +6,9 @@ import android.content.IntentFilter; import android.os.Bundle; import android.util.SparseIntArray; +import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import com.quran.labs.androidquran.QuranApplication; import com.quran.labs.androidquran.R; @@ -22,6 +23,8 @@ import com.quran.labs.androidquran.service.util.DefaultDownloadReceiver; import com.quran.labs.androidquran.service.util.QuranDownloadNotifier; import com.quran.labs.androidquran.service.util.ServiceIntentHelper; +import com.quran.labs.androidquran.ui.adapter.DownloadedItemActionListener; +import com.quran.labs.androidquran.ui.adapter.DownloadedMenuActionListener; import com.quran.labs.androidquran.ui.adapter.TranslationsAdapter; import com.quran.labs.androidquran.util.QuranFileUtils; import com.quran.labs.androidquran.util.QuranSettings; @@ -36,6 +39,7 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.view.ActionMode; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -44,7 +48,7 @@ import timber.log.Timber; public class TranslationManagerActivity extends QuranActionBarActivity - implements DefaultDownloadReceiver.SimpleDownloadListener { + implements DefaultDownloadReceiver.SimpleDownloadListener, DownloadedMenuActionListener { public static final String TRANSLATION_DOWNLOAD_KEY = "TRANSLATION_DOWNLOAD_KEY"; private static final String UPGRADING_EXTENSION = ".old"; @@ -63,6 +67,10 @@ public class TranslationManagerActivity extends QuranActionBarActivity private Disposable onClickRankUpDisposable; private Disposable onClickRankDownDisposable; + private ActionMode actionMode; + private TranslationSelectionListener selectionListener; + private DownloadedItemActionListener downloadedItemActionListener; + @Inject TranslationManagerPresenter presenter; @Inject QuranFileUtils quranFileUtils; @@ -82,6 +90,7 @@ public void onCreate(Bundle savedInstanceState) { adapter = new TranslationsAdapter(this); translationRecycler.setAdapter(adapter); + selectionListener = new TranslationSelectionListener(adapter); databaseDirectory = quranFileUtils.getQuranDatabaseDirectory(this); @@ -361,10 +370,10 @@ private void rankDownItem(TranslationRowData targetRow) { List sortedDownloads = sortedDownloadedItems(); if (sortedDownloads.indexOf(targetItem) + 1 < sortedDownloads.size()) { // ignore last item in list TranslationItem updatedItem = targetItem.withDisplayOrder(targetItem.getDisplayOrder() + 1); - ArrayList toUpdate = new ArrayList<> ( ); + ArrayList toUpdate = new ArrayList<>(); toUpdate.add(updatedItem); TranslationItem swapItem = null; - for ( TranslationItem translationItem : sortedDownloads ) { + for(TranslationItem translationItem : sortedDownloads){ if (translationItem.getDisplayOrder() == updatedItem.getDisplayOrder()) { swapItem = translationItem; break; @@ -381,7 +390,7 @@ private void rankDownItem(TranslationRowData targetRow) { } } } - for ( TranslationItem toUpdateItem : toUpdate ) { + for(TranslationItem toUpdateItem : toUpdate){ updateTranslationItem(toUpdateItem); } generateListItems(); @@ -432,4 +441,85 @@ private boolean removeTranslation(String fileName) { return false; } + @Override + public void startMenuAction(TranslationItem item, DownloadedItemActionListener aDownloadedItemActionListener) { + downloadedItemActionListener = aDownloadedItemActionListener; + if (actionMode != null) { + actionMode.finish(); + selectionListener.clearSelection(); + } else { + selectionListener.handleSelection(item); + actionMode = startSupportActionMode(new ModeCallback()); + } + } + + @Override + public void finishMenuAction() { + if (actionMode != null) { + actionMode.finish(); + } + selectionListener.clearSelection(); + downloadedItemActionListener = null; + } + + class TranslationSelectionListener { + private final TranslationsAdapter adapter; + + TranslationSelectionListener(TranslationsAdapter anAdapter) { + adapter = anAdapter; + } + + void handleSelection(TranslationItem item) { + adapter.setSelectedItem(item); + } + + void clearSelection() { + adapter.setSelectedItem(null); + } + } + + private class ModeCallback implements ActionMode.Callback { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.downloaded_translation_menu, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch(item.getItemId()) { + case R.id.dtm_delete: + if (downloadedItemActionListener != null) downloadedItemActionListener.handleDeleteItemAction(); + endAction(); + break; + case R.id.dtm_move_up: + if (downloadedItemActionListener != null) downloadedItemActionListener.handleRankUpItemAction(); + endAction(); + break; + case R.id.dtm_move_down: + if (downloadedItemActionListener != null) downloadedItemActionListener.handleRankDownItemAction(); + endAction(); + break; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + if (mode == actionMode) actionMode = null; + } + + private void endAction() { + if (actionMode != null) { + selectionListener.clearSelection(); + actionMode.finish(); + } + } + } } diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedItemActionListener.java b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedItemActionListener.java new file mode 100644 index 0000000000..9b97cdbd9e --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedItemActionListener.java @@ -0,0 +1,7 @@ +package com.quran.labs.androidquran.ui.adapter; + +public interface DownloadedItemActionListener { + void handleDeleteItemAction(); + void handleRankUpItemAction(); + void handleRankDownItemAction(); +} diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedMenuActionListener.java b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedMenuActionListener.java new file mode 100644 index 0000000000..8fe54cff03 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/DownloadedMenuActionListener.java @@ -0,0 +1,8 @@ +package com.quran.labs.androidquran.ui.adapter; + +import com.quran.labs.androidquran.dao.translation.TranslationItem; + +public interface DownloadedMenuActionListener { + void startMenuAction(TranslationItem item, DownloadedItemActionListener downloadedItemActionListener); + void finishMenuAction(); +} diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java index 3cdd039fb0..5279f5a0a6 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java @@ -2,9 +2,6 @@ import android.text.TextUtils; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -20,8 +17,6 @@ import java.util.List; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.view.ActionMode; import androidx.recyclerview.widget.RecyclerView; import io.reactivex.Observable; import io.reactivex.subjects.UnicastSubject; @@ -33,20 +28,16 @@ public class TranslationsAdapter extends RecyclerView.Adapter onClickRankUpSubject = UnicastSubject.create(); private final UnicastSubject onClickRankDownSubject = UnicastSubject.create(); - private final AppCompatActivity activity; - private final TranslationSelectionListener selectionListener; + private final DownloadedMenuActionListener downloadedMenuActionListener; private final DownloadedItemActionListener downloadedItemActionListener; private List translations = new ArrayList<>(); - private ActionMode actionMode; - private TranslationItem selectedItem; - public TranslationsAdapter(AppCompatActivity anActivity) { - this.activity = anActivity; - this.selectionListener = new TranslationSelectionListener(this); - this.downloadedItemActionListener = new DownloadedItemActionListener(); + public TranslationsAdapter(DownloadedMenuActionListener aDownloadedMenuActionListener) { + this.downloadedMenuActionListener = aDownloadedMenuActionListener; + this.downloadedItemActionListener = new DownloadedItemActionListenerImpl(); } @NotNull @@ -141,34 +132,18 @@ public void setSelectedItem ( TranslationItem selectedItem ) { notifyDataSetChanged(); } - class TranslationSelectionListener { - private final TranslationsAdapter adapter; - - TranslationSelectionListener(TranslationsAdapter anAdapter) { - adapter = anAdapter; - } - - void handleSelection(TranslationItem item) { - adapter.setSelectedItem(item); - } - - void clearSelection() { - adapter.setSelectedItem(null); - } - } - - class DownloadedItemActionListener { - void handleDeleteItemAction() { + class DownloadedItemActionListenerImpl implements DownloadedItemActionListener { + public void handleDeleteItemAction() { onClickRemoveSubject.onNext(selectedItem); selectedItem = null; } - void handleRankUpItemAction() { + public void handleRankUpItemAction() { onClickRankUpSubject.onNext(selectedItem); selectedItem = null; } - void handleRankDownItemAction() { + public void handleRankDownItemAction() { onClickRankDownSubject.onNext(selectedItem); selectedItem = null; } @@ -220,74 +195,16 @@ ImageView getRightImage() { } final View.OnLongClickListener actionMenuListener = v -> { - if (actionMode != null) { - actionMode.finish(); - selectionListener.clearSelection(); - } else if (activity != null) { - selectionListener.handleSelection(item); - actionMode = activity.startSupportActionMode(new ModeCallback()); - } + downloadedMenuActionListener.startMenuAction(item, downloadedItemActionListener); return true; }; @Override public void onClick(View v) { - if (actionMode != null) { - actionMode.finish(); - } - selectionListener.clearSelection(); + downloadedMenuActionListener.finishMenuAction(); if (!item.exists() || item.needsUpgrade()) { onClickDownloadSubject.onNext(item); } } } - - private class ModeCallback implements ActionMode.Callback { - @Override - public boolean onCreateActionMode ( ActionMode mode, Menu menu ) - { - MenuInflater inflater = activity.getMenuInflater(); - inflater.inflate(R.menu.downloaded_translation_menu, menu); - return true; - } - - @Override - public boolean onPrepareActionMode ( ActionMode mode, Menu menu ) - { - return false; - } - - @Override - public boolean onActionItemClicked ( ActionMode mode, MenuItem item ) - { - switch(item.getItemId ()) { - case R.id.dtm_delete: - downloadedItemActionListener.handleDeleteItemAction(); - endAction(); - break; - case R.id.dtm_move_up: - downloadedItemActionListener.handleRankUpItemAction(); - endAction(); - break; - case R.id.dtm_move_down: - downloadedItemActionListener.handleRankDownItemAction(); - endAction(); - break; - } - return false; - } - - @Override - public void onDestroyActionMode ( ActionMode mode ) - { - if (mode == actionMode) actionMode = null; - } - - private void endAction() { - if (actionMode != null) { - selectionListener.clearSelection(); - actionMode.finish(); - } - } - } } diff --git a/app/src/main/res/menu/downloaded_translation_menu.xml b/app/src/main/res/menu/downloaded_translation_menu.xml index 79c827edeb..fcb36c335b 100644 --- a/app/src/main/res/menu/downloaded_translation_menu.xml +++ b/app/src/main/res/menu/downloaded_translation_menu.xml @@ -3,14 +3,14 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ca3bbbef7a..1709cff7f9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -237,9 +237,9 @@ Error exporting data Data exported to %1$s - Move Up - Move Down - Remove Translation + Move Up + Move Down + Remove Translation quranandroid+logs@gmail.com Warning From 25dd8898ba5eff35e2a2d855644145f6f8e7e624 Mon Sep 17 00:00:00 2001 From: ismailjones Date: Fri, 17 Jul 2020 09:02:43 -0400 Subject: [PATCH 3/5] Initialize Cursor --- .../database/TranslationsDBAdapter.java | 2 +- .../database/TranslationsDBHelper.java | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java index 2fa38c957e..1521bdba8d 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java @@ -126,7 +126,7 @@ public boolean writeTranslationUpdates(List updates) { TranslationsTable.DISPLAY_ORDER + " DESC", "1" ); - if (cursor != null && cursor.moveToNext()) { + if (cursor != null && cursor.moveToFirst()) { displayOrder = cursor.getInt(0) + 1; } cursor.close(); diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java index 7d9fa03d3d..a0e58af2ac 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java @@ -86,16 +86,18 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Cursor translations = db.query( TranslationsTable.TABLE_NAME, new String[] { TranslationsTable.ID }, null, null, null, null, null ); - for (int i = 0; i < translations.getCount(); i++) { - ContentValues values = new ContentValues(); - values.put(TranslationsTable.DISPLAY_ORDER, i); - db.update( - TranslationsTable.TABLE_NAME, - values, - TranslationsTable.ID + " = ?", - new String[] { String.valueOf(translations.getInt(0)) } - ); - translations.moveToNext(); + if (translations != null && translations.moveToFirst()) { + for (int i = 0; i < translations.getCount(); i++) { + ContentValues values = new ContentValues(); + values.put(TranslationsTable.DISPLAY_ORDER, i); + db.update( + TranslationsTable.TABLE_NAME, + values, + TranslationsTable.ID + " = ?", + new String[] { String.valueOf(translations.getInt(0)) } + ); + translations.moveToNext(); + } } translations.close(); db.setTransactionSuccessful(); From b7f5aca29ad808af53827e29e12b533d481f54d6 Mon Sep 17 00:00:00 2001 From: ismailjones Date: Thu, 23 Jul 2020 11:27:36 -0400 Subject: [PATCH 4/5] cleanup and multi-move --- .../common/LocalTransationDiplaySort.kt | 3 +- .../translation/TranslationItemDiplaySort.kt | 8 ---- .../translation/TranslationItemDisplaySort.kt | 7 +++ .../database/TranslationsDBAdapter.java | 44 +++++++++++-------- .../database/TranslationsDBHelper.java | 27 +++++++----- .../ui/TranslationManagerActivity.java | 26 ++++++----- .../ui/adapter/TranslationsAdapter.java | 7 ++- 7 files changed, 67 insertions(+), 55 deletions(-) delete mode 100644 app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt create mode 100644 app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDisplaySort.kt diff --git a/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt index 3d19b21020..e21b55b9d7 100644 --- a/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt +++ b/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt @@ -1,8 +1,7 @@ package com.quran.labs.androidquran.common class LocalTransationDiplaySort : Comparator { - override fun compare(p0: LocalTranslation?, p1: LocalTranslation?): Int { - if (p0 == null || p1 == null) return 0; + override fun compare(p0: LocalTranslation, p1: LocalTranslation): Int { return p0.displayOrder.compareTo(p1.displayOrder); } } diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt deleted file mode 100644 index 723294a114..0000000000 --- a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDiplaySort.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.quran.labs.androidquran.dao.translation - -class TranslationItemDiplaySort : Comparator { - override fun compare(p0: TranslationItem?, p1: TranslationItem?): Int { - if (p0 == null || p1 == null) return 0; - return p0.displayOrder.compareTo(p1.displayOrder); - } -} diff --git a/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDisplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDisplaySort.kt new file mode 100644 index 0000000000..52ab77c149 --- /dev/null +++ b/app/src/main/java/com/quran/labs/androidquran/dao/translation/TranslationItemDisplaySort.kt @@ -0,0 +1,7 @@ +package com.quran.labs.androidquran.dao.translation + +class TranslationItemDisplaySort : Comparator { + override fun compare(p0: TranslationItem, p1: TranslationItem): Int { + return p0.displayOrder.compareTo(p1.displayOrder); + } +} diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java index 1521bdba8d..a992af6e41 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBAdapter.java @@ -72,24 +72,27 @@ public List getTranslations() { null, null, null, null, null, TranslationsTable.ID + " ASC"); if (cursor != null) { - while (cursor.moveToNext()) { - int id = cursor.getInt(0); - String name = cursor.getString(1); - String translator = cursor.getString(2); - String translatorForeign = cursor.getString(3); - String filename = cursor.getString(4); - String url = cursor.getString(5); - String languageCode = cursor.getString(6); - int version = cursor.getInt(7); - int minimumVersion = cursor.getInt(8); - int displayOrder = cursor.getInt(9); - - if (quranFileUtils.hasTranslation(context, filename)) { - items.add(new LocalTranslation(id, filename, name, translator, - translatorForeign, url, languageCode, version, minimumVersion, displayOrder)); + try { + while (cursor.moveToNext()) { + int id = cursor.getInt(0); + String name = cursor.getString(1); + String translator = cursor.getString(2); + String translatorForeign = cursor.getString(3); + String filename = cursor.getString(4); + String url = cursor.getString(5); + String languageCode = cursor.getString(6); + int version = cursor.getInt(7); + int minimumVersion = cursor.getInt(8); + int displayOrder = cursor.getInt(9); + + if (quranFileUtils.hasTranslation(context, filename)) { + items.add(new LocalTranslation(id, filename, name, translator, + translatorForeign, url, languageCode, version, minimumVersion, displayOrder)); + } } + } finally { + cursor.close(); } - cursor.close(); } items = Collections.unmodifiableList(items); if (items.size() > 0) { @@ -126,10 +129,13 @@ public boolean writeTranslationUpdates(List updates) { TranslationsTable.DISPLAY_ORDER + " DESC", "1" ); - if (cursor != null && cursor.moveToFirst()) { - displayOrder = cursor.getInt(0) + 1; + try { + if (cursor != null && cursor.moveToFirst()) { + displayOrder = cursor.getInt(0) + 1; + } + } finally { + if (cursor !=null) cursor.close(); } - cursor.close(); } ContentValues values = new ContentValues(); diff --git a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java index a0e58af2ac..4d27b7b263 100644 --- a/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java +++ b/app/src/main/java/com/quran/labs/androidquran/database/TranslationsDBHelper.java @@ -86,20 +86,23 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Cursor translations = db.query( TranslationsTable.TABLE_NAME, new String[] { TranslationsTable.ID }, null, null, null, null, null ); - if (translations != null && translations.moveToFirst()) { - for (int i = 0; i < translations.getCount(); i++) { - ContentValues values = new ContentValues(); - values.put(TranslationsTable.DISPLAY_ORDER, i); - db.update( - TranslationsTable.TABLE_NAME, - values, - TranslationsTable.ID + " = ?", - new String[] { String.valueOf(translations.getInt(0)) } - ); - translations.moveToNext(); + try { + if (translations != null && translations.moveToFirst()) { + for (int i = 0; i < translations.getCount(); i++) { + ContentValues values = new ContentValues(); + values.put(TranslationsTable.DISPLAY_ORDER, i); + db.update( + TranslationsTable.TABLE_NAME, + values, + TranslationsTable.ID + " = ?", + new String[] { String.valueOf(translations.getInt(0)) } + ); + translations.moveToNext(); + } } + } finally { + if (translations != null) translations.close(); } - translations.close(); db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java index 71dcde8332..d8427ed1b8 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/TranslationManagerActivity.java @@ -15,7 +15,7 @@ import com.quran.labs.androidquran.dao.translation.Translation; import com.quran.labs.androidquran.dao.translation.TranslationHeader; import com.quran.labs.androidquran.dao.translation.TranslationItem; -import com.quran.labs.androidquran.dao.translation.TranslationItemDiplaySort; +import com.quran.labs.androidquran.dao.translation.TranslationItemDisplaySort; import com.quran.labs.androidquran.dao.translation.TranslationRowData; import com.quran.labs.androidquran.database.DatabaseHandler; import com.quran.labs.androidquran.presenter.translation.TranslationManagerPresenter; @@ -251,7 +251,7 @@ private void generateListItems() { TranslationHeader hdr = new TranslationHeader(getString(R.string.downloaded_translations)); result.add(hdr); - Collections.sort(downloaded, new TranslationItemDiplaySort()); + Collections.sort(downloaded, new TranslationItemDisplaySort ()); boolean needsUpgrade = false; for (TranslationItem item : downloaded) { @@ -361,7 +361,7 @@ private List sortedDownloadedItems() { for (TranslationItem item : allItems) { if (item.exists()) result.add(item); } - Collections.sort(result, new TranslationItemDiplaySort()); + Collections.sort(result, new TranslationItemDisplaySort ()); return result; } @@ -390,10 +390,13 @@ private void rankDownItem(TranslationRowData targetRow) { } } } - for(TranslationItem toUpdateItem : toUpdate){ - updateTranslationItem(toUpdateItem); + if (!toUpdate.isEmpty()) { + if (selectionListener != null) selectionListener.handleSelection(updatedItem); + for(TranslationItem toUpdateItem : toUpdate){ + updateTranslationItem(toUpdateItem); + } + generateListItems(); } - generateListItems(); } } @@ -403,8 +406,9 @@ private void rankUpItem(TranslationRowData targetRow) { if (sortedDownloads.indexOf(targetItem) > 0) { // ignore first item in list ArrayList toUpdate = new ArrayList<>(); int updatedDisplayOrder = targetItem.getDisplayOrder(); + TranslationItem updatedItem = null; if (updatedDisplayOrder > 0) { - TranslationItem updatedItem = targetItem.withDisplayOrder(--updatedDisplayOrder); + updatedItem = targetItem.withDisplayOrder(--updatedDisplayOrder); toUpdate.add(updatedItem); } TranslationItem swapItem = null; @@ -423,6 +427,7 @@ private void rankUpItem(TranslationRowData targetRow) { } } if (!toUpdate.isEmpty()) { + if (selectionListener != null && updatedItem != null) selectionListener.handleSelection(updatedItem); for (TranslationItem toUpdateItem : toUpdate) { updateTranslationItem(toUpdateItem); } @@ -500,11 +505,9 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { break; case R.id.dtm_move_up: if (downloadedItemActionListener != null) downloadedItemActionListener.handleRankUpItemAction(); - endAction(); break; case R.id.dtm_move_down: if (downloadedItemActionListener != null) downloadedItemActionListener.handleRankDownItemAction(); - endAction(); break; } return false; @@ -512,7 +515,10 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { @Override public void onDestroyActionMode(ActionMode mode) { - if (mode == actionMode) actionMode = null; + if (mode == actionMode) { + selectionListener.clearSelection(); + actionMode = null; + } } private void endAction() { diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java index 5279f5a0a6..5ab56d42d7 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/adapter/TranslationsAdapter.java @@ -54,7 +54,9 @@ public void onBindViewHolder(@NotNull TranslationViewHolder holder, int position case R.layout.translation_row: TranslationItem item = (TranslationItem) rowItem; holder.setItem(item); - holder.itemView.setActivated(item.equals(selectedItem)); + holder.itemView.setActivated( + (selectedItem != null) && (item.getTranslation().getId() == selectedItem.getTranslation().getId()) + ); holder.getTranslationTitle().setText(item.name()); if (TextUtils.isEmpty(item.getTranslation().getTranslatorNameLocalized())) { holder.getTranslationInfo().setText(item.getTranslation().getTranslator()); @@ -135,17 +137,14 @@ public void setSelectedItem ( TranslationItem selectedItem ) { class DownloadedItemActionListenerImpl implements DownloadedItemActionListener { public void handleDeleteItemAction() { onClickRemoveSubject.onNext(selectedItem); - selectedItem = null; } public void handleRankUpItemAction() { onClickRankUpSubject.onNext(selectedItem); - selectedItem = null; } public void handleRankDownItemAction() { onClickRankDownSubject.onNext(selectedItem); - selectedItem = null; } } From 604b9bf451792f7ce88b76a26f99c1af04f8b592 Mon Sep 17 00:00:00 2001 From: ismailjones Date: Fri, 24 Jul 2020 09:24:54 -0400 Subject: [PATCH 5/5] spelling --- ...TransationDiplaySort.kt => LocalTranslationDisplaySort.kt} | 2 +- .../java/com/quran/labs/androidquran/ui/PagerActivity.java | 4 ++-- .../labs/androidquran/ui/translation/TranslationView.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/src/main/java/com/quran/labs/androidquran/common/{LocalTransationDiplaySort.kt => LocalTranslationDisplaySort.kt} (72%) diff --git a/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt b/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslationDisplaySort.kt similarity index 72% rename from app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt rename to app/src/main/java/com/quran/labs/androidquran/common/LocalTranslationDisplaySort.kt index e21b55b9d7..888836241f 100644 --- a/app/src/main/java/com/quran/labs/androidquran/common/LocalTransationDiplaySort.kt +++ b/app/src/main/java/com/quran/labs/androidquran/common/LocalTranslationDisplaySort.kt @@ -1,6 +1,6 @@ package com.quran.labs.androidquran.common -class LocalTransationDiplaySort : Comparator { +class LocalTranslationDisplaySort : Comparator { override fun compare(p0: LocalTranslation, p1: LocalTranslation): Int { return p0.displayOrder.compareTo(p1.displayOrder); } 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 b4b1a1968d..3a51f9af9f 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 @@ -37,7 +37,7 @@ import com.quran.labs.androidquran.QuranPreferenceActivity; import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.SearchActivity; -import com.quran.labs.androidquran.common.LocalTransationDiplaySort; +import com.quran.labs.androidquran.common.LocalTranslationDisplaySort; import com.quran.labs.androidquran.common.LocalTranslation; import com.quran.labs.androidquran.common.audio.QariItem; import com.quran.labs.androidquran.di.component.activity.PagerActivityComponent; @@ -1358,7 +1358,7 @@ private void requestTranslationsList() { @Override public void onSuccess(List translationList) { final List sortedTranslations = new ArrayList<>(translationList); - Collections.sort(sortedTranslations, new LocalTransationDiplaySort()); + Collections.sort(sortedTranslations, new LocalTranslationDisplaySort()); int items = sortedTranslations.size(); String[] titles = new String[items]; diff --git a/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java b/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java index 67377da203..e720d4c6be 100644 --- a/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java +++ b/app/src/main/java/com/quran/labs/androidquran/ui/translation/TranslationView.java @@ -9,7 +9,7 @@ import com.quran.labs.androidquran.BuildConfig; import com.quran.labs.androidquran.R; -import com.quran.labs.androidquran.common.LocalTransationDiplaySort; +import com.quran.labs.androidquran.common.LocalTranslationDisplaySort; import com.quran.labs.androidquran.common.LocalTranslation; import com.quran.labs.androidquran.common.QuranAyahInfo; import com.quran.labs.androidquran.common.TranslationMetadata; @@ -110,7 +110,7 @@ public void setVerses(@NonNull QuranDisplayData quranDisplayData, } final LocalTranslation[] sortedTranslations = Arrays.copyOf(this.translations, this.translations.length); - Arrays.sort(sortedTranslations, new LocalTransationDiplaySort()); + Arrays.sort(sortedTranslations, new LocalTranslationDisplaySort()); for (int j = 0; j < sortedTranslations.length; j++) { final TranslationMetadata metadata = findText(verse.texts, sortedTranslations[j].getId());