mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	[feature] add ability to set global filter/sort/display for Manga chapters (#3622)
* - [feature] add ability to set global filter/sort/display for Manga chapters * - move default chapter settings functionality to overflow menu - code clean up * - show confirmation dialog when user selects "Set as Default" option in Chapter Settings * - hide overflow menu in LibrarySettingsSheet * - apply default chapter settings if manga is added to Library from a Source's browsing screen Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
		| @@ -83,6 +83,11 @@ interface MangaQueries : DbProvider { | ||||
|         .withPutResolver(MangaFlagsPutResolver()) | ||||
|         .prepare() | ||||
|  | ||||
|     fun updateFlags(mangas: List<Manga>) = db.put() | ||||
|         .objects(mangas) | ||||
|         .withPutResolver(MangaFlagsPutResolver(true)) | ||||
|         .prepare() | ||||
|  | ||||
|     fun updateLastUpdated(manga: Manga) = db.put() | ||||
|         .`object`(manga) | ||||
|         .withPutResolver(MangaLastUpdatedPutResolver()) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.data.database.inTransactionReturn | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.database.tables.MangaTable | ||||
|  | ||||
| class MangaFlagsPutResolver : PutResolver<Manga>() { | ||||
| class MangaFlagsPutResolver(private val updateAll: Boolean = false) : PutResolver<Manga>() { | ||||
|  | ||||
|     override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { | ||||
|         val updateQuery = mapToUpdateQuery(manga) | ||||
| @@ -19,11 +19,21 @@ class MangaFlagsPutResolver : PutResolver<Manga>() { | ||||
|         PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table()) | ||||
|     } | ||||
|  | ||||
|     fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() | ||||
|         .table(MangaTable.TABLE) | ||||
|         .where("${MangaTable.COL_ID} = ?") | ||||
|         .whereArgs(manga.id) | ||||
|         .build() | ||||
|     fun mapToUpdateQuery(manga: Manga): UpdateQuery { | ||||
|         val builder = UpdateQuery.builder() | ||||
|  | ||||
|         return if (updateAll) { | ||||
|             builder | ||||
|                 .table(MangaTable.TABLE) | ||||
|                 .build() | ||||
|         } else { | ||||
|             builder | ||||
|                 .table(MangaTable.TABLE) | ||||
|                 .where("${MangaTable.COL_ID} = ?") | ||||
|                 .whereArgs(manga.id) | ||||
|                 .build() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun mapToContentValues(manga: Manga) = ContentValues(1).apply { | ||||
|         put(MangaTable.COL_CHAPTER_FLAGS, manga.chapter_flags) | ||||
|   | ||||
| @@ -165,6 +165,18 @@ object PreferenceKeys { | ||||
|  | ||||
|     const val enableDoh = "enable_doh" | ||||
|  | ||||
|     const val defaultChapterFilterByRead = "default_chapter_filter_by_read" | ||||
|  | ||||
|     const val defaultChapterFilterByDownloaded = "default_chapter_filter_by_downloaded" | ||||
|  | ||||
|     const val defaultChapterFilterByBookmarked = "default_chapter_filter_by_bookmarked" | ||||
|  | ||||
|     const val defaultChapterSortBySourceOrNumber = "default_chapter_sort_by_source_or_number" // and upload date | ||||
|  | ||||
|     const val defaultChapterSortByAscendingOrDescending = "default_chapter_sort_by_ascending_or_descending" | ||||
|  | ||||
|     const val defaultChapterDisplayByNameOrNumber = "default_chapter_display_by_name_or_number" | ||||
|  | ||||
|     fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId" | ||||
|  | ||||
|     fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId" | ||||
|   | ||||
| @@ -2,11 +2,15 @@ package eu.kanade.tachiyomi.data.preference | ||||
|  | ||||
| import android.content.Context | ||||
| import android.os.Environment | ||||
| import androidx.core.content.edit | ||||
| import androidx.core.net.toUri | ||||
| import androidx.preference.PreferenceManager | ||||
| import com.tfcporciuncula.flow.FlowSharedPreferences | ||||
| import com.tfcporciuncula.flow.Preference | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceValues.DisplayMode | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceValues.NsfwAllowance | ||||
| import eu.kanade.tachiyomi.data.track.TrackService | ||||
| @@ -18,8 +22,6 @@ import java.io.File | ||||
| import java.text.DateFormat | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.Locale | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values | ||||
|  | ||||
| @OptIn(ExperimentalCoroutinesApi::class) | ||||
| fun <T> Preference<T>.asImmediateFlow(block: (value: T) -> Unit): Flow<T> { | ||||
| @@ -249,4 +251,29 @@ class PreferencesHelper(val context: Context) { | ||||
|     fun trustedSignatures() = flowPrefs.getStringSet("trusted_signatures", emptySet()) | ||||
|  | ||||
|     fun enableDoh() = prefs.getBoolean(Keys.enableDoh, false) | ||||
|  | ||||
|     fun filterChapterByRead() = prefs.getInt(Keys.defaultChapterFilterByRead, Manga.SHOW_ALL) | ||||
|  | ||||
|     fun filterChapterByDownloaded() = prefs.getInt(Keys.defaultChapterFilterByDownloaded, Manga.SHOW_ALL) | ||||
|  | ||||
|     fun filterChapterByBookmarked() = prefs.getInt(Keys.defaultChapterFilterByBookmarked, Manga.SHOW_ALL) | ||||
|  | ||||
|     fun sortChapterBySourceOrNumber() = prefs.getInt(Keys.defaultChapterSortBySourceOrNumber, Manga.SORTING_SOURCE) | ||||
|  | ||||
|     fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, Manga.DISPLAY_NAME) | ||||
|  | ||||
|     fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.SORT_DESC) | ||||
|  | ||||
|     fun setChapterSettingsDefault(m: Manga) { | ||||
|         prefs.edit { | ||||
|             putInt(Keys.defaultChapterFilterByRead, m.readFilter) | ||||
|             putInt(Keys.defaultChapterFilterByDownloaded, m.downloadedFilter) | ||||
|             putInt(Keys.defaultChapterFilterByBookmarked, m.bookmarkedFilter) | ||||
|             putInt(Keys.defaultChapterSortBySourceOrNumber, m.sorting) | ||||
|             putInt(Keys.defaultChapterDisplayByNameOrNumber, m.displayMode) | ||||
|         } | ||||
|  | ||||
|         if (m.sortDescending()) prefs.edit { putInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.SORT_DESC) } | ||||
|         else prefs.edit { putInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.SORT_ASC) } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TextItem | ||||
| import eu.kanade.tachiyomi.ui.browse.source.filter.TextSectionItem | ||||
| import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem | ||||
| import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem | ||||
| import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper | ||||
| import eu.kanade.tachiyomi.util.removeCovers | ||||
| import kotlinx.coroutines.flow.subscribe | ||||
| import rx.Observable | ||||
| @@ -268,6 +269,8 @@ open class BrowseSourcePresenter( | ||||
|  | ||||
|         if (!manga.favorite) { | ||||
|             manga.removeCovers(coverCache) | ||||
|         } else { | ||||
|             ChapterSettingsHelper.applySettingDefaultsFromPreferences(manga) | ||||
|         } | ||||
|  | ||||
|         db.insertManga(manga).executeAsBlocking() | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.source.LocalSource | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter | ||||
| import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem | ||||
| import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper | ||||
| import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource | ||||
| import eu.kanade.tachiyomi.util.isLocal | ||||
| import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed | ||||
| @@ -82,6 +83,10 @@ class MangaPresenter( | ||||
|     override fun onCreate(savedState: Bundle?) { | ||||
|         super.onCreate(savedState) | ||||
|  | ||||
|         if (!manga.favorite) { | ||||
|             ChapterSettingsHelper.applySettingDefaultsFromPreferences(manga) | ||||
|         } | ||||
|  | ||||
|         // Manga info - start | ||||
|  | ||||
|         getMangaObservable() | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class ChaptersSettingsSheet( | ||||
|     activity: Activity, | ||||
|     private val presenter: MangaPresenter, | ||||
|     onGroupClickListener: (ExtendedNavigationView.Group) -> Unit | ||||
| ) : TabbedBottomSheetDialog(activity) { | ||||
| ) : TabbedBottomSheetDialog(activity, presenter.manga) { | ||||
|  | ||||
|     val filters: Filter | ||||
|     private val sort: Sort | ||||
|   | ||||
| @@ -0,0 +1,46 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.chapter | ||||
|  | ||||
| import android.app.Dialog | ||||
| import android.content.Context | ||||
| import com.afollestad.materialdialogs.MaterialDialog | ||||
| import com.afollestad.materialdialogs.customview.customView | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| import eu.kanade.tachiyomi.widget.DialogCheckboxView | ||||
|  | ||||
| class SetChapterSettingsDialog(val context: Context, val manga: Manga) { | ||||
|     private var dialog: Dialog | ||||
|  | ||||
|     init { | ||||
|         this.dialog = buildDialog() | ||||
|     } | ||||
|  | ||||
|     private fun buildDialog(): Dialog { | ||||
|         val view = DialogCheckboxView(context).apply { | ||||
|             setDescription(R.string.confirm_set_chapter_settings) | ||||
|             setOptionDescription(R.string.also_set_chapter_settings_for_library) | ||||
|         } | ||||
|  | ||||
|         return MaterialDialog(context) | ||||
|             .title(R.string.action_chapter_settings) | ||||
|             .customView( | ||||
|                 view = view, | ||||
|                 horizontalPadding = true | ||||
|             ) | ||||
|             .positiveButton(android.R.string.ok) { | ||||
|                 ChapterSettingsHelper.setNewSettingDefaults(manga) | ||||
|                 if (view.isChecked()) { | ||||
|                     ChapterSettingsHelper.updateAllMangasWithDefaultsFromPreferences() | ||||
|                 } | ||||
|  | ||||
|                 context.toast(context.getString(R.string.chapter_settings_updated)) | ||||
|             } | ||||
|             .negativeButton(android.R.string.cancel) | ||||
|     } | ||||
|  | ||||
|     fun showDialog() = dialog.show() | ||||
|  | ||||
|     fun dismissDialog() = dialog.dismiss() | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| package eu.kanade.tachiyomi.util.chapter | ||||
|  | ||||
| import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.util.lang.launchIO | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| object ChapterSettingsHelper { | ||||
|     private val prefs = Injekt.get<PreferencesHelper>() | ||||
|     private val db: DatabaseHelper = Injekt.get() | ||||
|  | ||||
|     /** | ||||
|      * updates the Chapter Settings in Preferences | ||||
|      */ | ||||
|     fun setNewSettingDefaults(m: Manga?) { | ||||
|         m?.let { | ||||
|             prefs.setChapterSettingsDefault(it) | ||||
|             db.updateFlags(it).executeAsBlocking() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * updates a single manga's Chapter Settings to match what's set in Preferences | ||||
|      */ | ||||
|     fun applySettingDefaultsFromPreferences(m: Manga) { | ||||
|         m.readFilter = prefs.filterChapterByRead() | ||||
|         m.downloadedFilter = prefs.filterChapterByDownloaded() | ||||
|         m.bookmarkedFilter = prefs.filterChapterByBookmarked() | ||||
|         m.sorting = prefs.sortChapterBySourceOrNumber() | ||||
|         m.displayMode = prefs.displayChapterByNameOrNumber() | ||||
|         m.setChapterOrder(prefs.sortChapterByAscendingOrDescending()) | ||||
|         db.updateFlags(m).executeAsBlocking() | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * updates all mangas in database Chapter Settings to match what's set in Preferences | ||||
|      */ | ||||
|     fun updateAllMangasWithDefaultsFromPreferences() { | ||||
|         launchIO { | ||||
|             val dbMangas = db.getMangas().executeAsBlocking().toMutableList() | ||||
|  | ||||
|             val updatedMangas = dbMangas.map { m -> | ||||
|                 m.readFilter = prefs.filterChapterByRead() | ||||
|                 m.downloadedFilter = prefs.filterChapterByDownloaded() | ||||
|                 m.bookmarkedFilter = prefs.filterChapterByBookmarked() | ||||
|                 m.sorting = prefs.sortChapterBySourceOrNumber() | ||||
|                 m.displayMode = prefs.displayChapterByNameOrNumber() | ||||
|                 m.setChapterOrder(prefs.sortChapterByAscendingOrDescending()) | ||||
|                 m | ||||
|             }.toList() | ||||
|  | ||||
|             db.updateFlags(updatedMangas).executeAsBlocking() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -64,7 +64,7 @@ inline fun View.popupMenu( | ||||
|     @MenuRes menuRes: Int, | ||||
|     noinline initMenu: (Menu.() -> Unit)? = null, | ||||
|     noinline onMenuItemClick: MenuItem.() -> Boolean | ||||
| ) { | ||||
| ): PopupMenu { | ||||
|     val popup = PopupMenu(context, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0) | ||||
|     popup.menuInflater.inflate(menuRes, popup.menu) | ||||
|  | ||||
| @@ -74,6 +74,7 @@ inline fun View.popupMenu( | ||||
|     popup.setOnMenuItemClickListener { it.onMenuItemClick() } | ||||
|  | ||||
|     popup.show() | ||||
|     return popup | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -4,21 +4,49 @@ import android.app.Activity | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import com.google.android.material.bottomsheet.BottomSheetDialog | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding | ||||
| import eu.kanade.tachiyomi.ui.manga.chapter.SetChapterSettingsDialog | ||||
| import eu.kanade.tachiyomi.util.view.popupMenu | ||||
|  | ||||
| abstract class TabbedBottomSheetDialog(private val activity: Activity) : BottomSheetDialog(activity) { | ||||
| abstract class TabbedBottomSheetDialog(private val activity: Activity, private val manga: Manga? = null) : BottomSheetDialog(activity) { | ||||
|     val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(activity.layoutInflater) | ||||
|  | ||||
|     init { | ||||
|         val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(activity.layoutInflater) | ||||
|  | ||||
|         val adapter = LibrarySettingsSheetAdapter() | ||||
|         binding.pager.offscreenPageLimit = 2 | ||||
|         binding.pager.adapter = adapter | ||||
|         binding.tabs.setupWithViewPager(binding.pager) | ||||
|  | ||||
|         // currently, we only need to show the overflow menu if this is a ChaptersSettingsSheet | ||||
|         if (manga != null) { | ||||
|             binding.menu.visibility = View.VISIBLE | ||||
|             binding.menu.setOnClickListener { it.post { showPopupMenu(it) } } | ||||
|         } else { | ||||
|             binding.menu.visibility = View.GONE | ||||
|         } | ||||
|  | ||||
|         setContentView(binding.root) | ||||
|     } | ||||
|  | ||||
|     private fun showPopupMenu(view: View) { | ||||
|         view.popupMenu( | ||||
|             R.menu.default_chapter_filter, | ||||
|             { | ||||
|             }, | ||||
|             { | ||||
|                 when (this.itemId) { | ||||
|                     R.id.save_as_default -> { | ||||
|                         manga?.let { SetChapterSettingsDialog(context, it).showDialog() } | ||||
|                         true | ||||
|                     } | ||||
|                     else -> true | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     abstract fun getTabViews(): List<View> | ||||
|  | ||||
|     abstract fun getTabTitles(): List<Int> | ||||
|   | ||||
| @@ -1,17 +1,42 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="vertical"> | ||||
|  | ||||
|     <com.google.android.material.tabs.TabLayout | ||||
|         android:id="@+id/tabs" | ||||
|         style="@style/Theme.Widget.Tabs" | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:tabGravity="fill" | ||||
|         app:tabMode="fixed" /> | ||||
|         android:layout_height="wrap_content"> | ||||
|  | ||||
|         <com.google.android.material.tabs.TabLayout | ||||
|             android:id="@+id/tabs" | ||||
|             style="@style/Theme.Widget.Tabs" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toStartOf="@+id/menu" | ||||
|             app:layout_constraintStart_toStartOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" | ||||
|             app:tabGravity="fill" | ||||
|             app:tabMode="fixed" /> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/menu" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:background="?selectableItemBackgroundBorderless" | ||||
|             android:contentDescription="@string/action_menu" | ||||
|             android:paddingStart="10dp" | ||||
|             android:paddingEnd="10dp" | ||||
|             app:layout_constraintBottom_toBottomOf="parent" | ||||
|             app:layout_constraintEnd_toEndOf="parent" | ||||
|             app:layout_constraintTop_toTopOf="parent" | ||||
|             app:srcCompat="@drawable/ic_more_vert_24dp" | ||||
|             app:tint="?attr/colorOnBackground" /> | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.MaxHeightViewPager | ||||
|         android:id="@+id/pager" | ||||
|   | ||||
							
								
								
									
										8
									
								
								app/src/main/res/menu/default_chapter_filter.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/menu/default_chapter_filter.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <item | ||||
|         android:id="@+id/save_as_default" | ||||
|         android:title="@string/set_chapter_settings_as_default" /> | ||||
|  | ||||
| </menu> | ||||
| @@ -30,6 +30,7 @@ | ||||
|  | ||||
|     <!-- Actions --> | ||||
|     <string name="action_settings">Settings</string> | ||||
|     <string name="action_chapter_settings">Chapter Settings</string> | ||||
|     <string name="action_menu">Menu</string> | ||||
|     <string name="action_filter">Filter</string> | ||||
|     <string name="action_filter_downloaded">Downloaded</string> | ||||
| @@ -524,6 +525,9 @@ | ||||
|     <string name="download_unread">Unread</string> | ||||
|     <string name="confirm_delete_chapters">Are you sure you want to delete the selected chapters?</string> | ||||
|     <string name="invalid_download_dir">Invalid download location</string> | ||||
|     <string name="confirm_set_chapter_settings">Are you sure you want to save these settings as default?</string> | ||||
|     <string name="also_set_chapter_settings_for_library">Also apply to all manga in my Library</string> | ||||
|     <string name="set_chapter_settings_as_default">Set as Default</string> | ||||
|     <string name="no_chapters_error">No chapters found</string> | ||||
|  | ||||
|     <!-- Tracking Screen --> | ||||
| @@ -678,6 +682,7 @@ | ||||
|     <string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string> | ||||
|     <string name="information_webview_required">WebView is required for Tachiyomi</string> | ||||
|     <string name="information_webview_outdated">Please update the WebView app for better compatibility</string> | ||||
|     <string name="chapter_settings_updated">Updated default chapter settings</string> | ||||
|  | ||||
|     <!-- Download Notification --> | ||||
|     <string name="download_notifier_downloader_title">Downloader</string> | ||||
| @@ -705,4 +710,5 @@ | ||||
|     <string name="tapping_inverted_horizontal">Horizontal</string> | ||||
|     <string name="tapping_inverted_vertical">Vertical</string> | ||||
|     <string name="tapping_inverted_both">Both</string> | ||||
|   | ||||
| </resources> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user