diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 358aa4090..7ea5f3ada 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -7,15 +7,35 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.preference.PreferenceManager import org.koitharu.kotatsu.R import org.koitharu.kotatsu.utils.delegates.prefs.EnumPreferenceDelegate +import org.koitharu.kotatsu.utils.delegates.prefs.NullableStringPreferenceDelegate import org.koitharu.kotatsu.utils.delegates.prefs.StringIntPreferenceDelegate -class AppSettings private constructor(resources: Resources, private val prefs: SharedPreferences) : SharedPreferences by prefs { +class AppSettings private constructor(resources: Resources, private val prefs: SharedPreferences) : + SharedPreferences by prefs { - constructor(context: Context) : this(context.resources, PreferenceManager.getDefaultSharedPreferences(context)) + constructor(context: Context) : this( + context.resources, + PreferenceManager.getDefaultSharedPreferences(context) + ) - var listMode by EnumPreferenceDelegate(ListMode::class.java, resources.getString(R.string.key_list_mode), ListMode.DETAILED_LIST) + var listMode by EnumPreferenceDelegate( + ListMode::class.java, + resources.getString(R.string.key_list_mode), + ListMode.DETAILED_LIST + ) - val theme by StringIntPreferenceDelegate(resources.getString(R.string.key_theme), AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + val theme by StringIntPreferenceDelegate( + resources.getString(R.string.key_theme), + AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + ) + + private var sourcesOrderStr by NullableStringPreferenceDelegate(resources.getString(R.string.key_sources_order)) + + var sourcesOrder: List + get() = sourcesOrderStr?.split('|')?.mapNotNull(String::toIntOrNull).orEmpty() + set(value) { + sourcesOrderStr = value.joinToString("|") + } fun subscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) { prefs.registerOnSharedPreferenceChangeListener(listener) diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/MangaProviderFactory.kt b/app/src/main/java/org/koitharu/kotatsu/domain/MangaProviderFactory.kt index d77243f59..af668d280 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/MangaProviderFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/MangaProviderFactory.kt @@ -1,14 +1,26 @@ package org.koitharu.kotatsu.domain import org.koin.core.KoinComponent +import org.koin.core.get import org.koin.core.inject import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.prefs.AppSettings object MangaProviderFactory : KoinComponent { private val loaderContext by inject() + val sources: List + get() { + val list = MangaSource.values().toList() - MangaSource.LOCAL + val order = get().sourcesOrder + return list.sortedBy { x -> + val e = order.indexOf(x.ordinal) + if (e == -1) order.size + x.ordinal else e + } + } + fun create(source: MangaSource): MangaRepository { val constructor = source.cls.getConstructor(MangaLoaderContext::class.java) return constructor.newInstance(loaderContext) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt index 3cf982810..abb53d92a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainActivity.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.ui.main +import android.content.SharedPreferences import android.content.res.Configuration import android.os.Bundle import android.view.Menu @@ -8,8 +9,11 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.fragment.app.Fragment import com.google.android.material.navigation.NavigationView import kotlinx.android.synthetic.main.activity_main.* +import org.koin.core.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.domain.MangaProviderFactory import org.koitharu.kotatsu.ui.common.BaseActivity import org.koitharu.kotatsu.ui.main.list.favourites.FavouritesListFragment import org.koitharu.kotatsu.ui.main.list.history.HistoryListFragment @@ -18,8 +22,10 @@ import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment import org.koitharu.kotatsu.ui.settings.SettingsActivity import org.koitharu.kotatsu.utils.SearchHelper -class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { +class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener, + SharedPreferences.OnSharedPreferenceChangeListener { + private val settings by inject() private lateinit var drawerToggle: ActionBarDrawerToggle override fun onCreate(savedInstanceState: Bundle?) { @@ -33,6 +39,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList supportActionBar?.setHomeButtonEnabled(true) navigationView.setNavigationItemSelectedListener(this) + settings.subscribe(this) if (supportFragmentManager.findFragmentById(R.id.container) == null) { navigationView.setCheckedItem(R.id.nav_history) @@ -40,10 +47,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } + override fun onDestroy() { + settings.unsubscribe(this) + super.onDestroy() + } + override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) drawerToggle.syncState() - initSideMenu(MangaSource.values().asList() - MangaSource.LOCAL) + initSideMenu(MangaProviderFactory.sources) } override fun onConfigurationChanged(newConfig: Configuration) { @@ -90,6 +102,12 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList submenu.setGroupCheckable(R.id.group_remote_sources, true, true) } + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + when (key) { + getString(R.string.key_sources_order) -> initSideMenu(MangaProviderFactory.sources) + } + } + private fun setPrimaryFragment(fragment: Fragment) { supportFragmentManager.beginTransaction() .replace(R.id.container, fragment) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/AppearanceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/AppearanceSettingsFragment.kt index 33e6ee0ac..3db873c75 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/AppearanceSettingsFragment.kt @@ -11,7 +11,6 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.ui.common.BasePreferenceFragment import org.koitharu.kotatsu.ui.main.list.ListModeSelectDialog -import org.koitharu.kotatsu.utils.ext.bindSummary class AppearanceSettingsFragment : BasePreferenceFragment(R.string.appearance), SharedPreferences.OnSharedPreferenceChangeListener { @@ -21,7 +20,7 @@ class AppearanceSettingsFragment : BasePreferenceFragment(R.string.appearance), findPreference(R.string.key_list_mode)?.summary = listModes[settings.listMode]?.let(::getString) - findPreference(R.string.key_theme)?.bindSummary() + findPreference(R.string.key_theme)?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt index 6888d2123..3e5f1c0f8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt @@ -3,7 +3,9 @@ package org.koitharu.kotatsu.ui.settings import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.fragment.app.commit import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.ui.common.BaseActivity class SettingsActivity : BaseActivity() { @@ -20,6 +22,13 @@ class SettingsActivity : BaseActivity() { } } + fun openMangaSourceSettings(mangaSource: MangaSource) { + supportFragmentManager.commit { + replace(R.id.container, SourceSettingsFragment.newInstance(mangaSource)) + addToBackStack(null) + } + } + companion object { fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SourceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SourceSettingsFragment.kt new file mode 100644 index 000000000..f87a51f5a --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SourceSettingsFragment.kt @@ -0,0 +1,35 @@ +package org.koitharu.kotatsu.ui.settings + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import org.koin.core.KoinComponent +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.utils.ext.withArgs + +class SourceSettingsFragment : PreferenceFragmentCompat(), KoinComponent { + + private lateinit var source: MangaSource + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + source = requireArguments().getParcelable(EXTRA_SOURCE)!! + } + + override fun onResume() { + super.onResume() + activity?.title = source.title + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + + } + + companion object { + + private const val EXTRA_SOURCE = "source" + + fun newInstance(source: MangaSource) = SourceSettingsFragment().withArgs(1) { + putParcelable(EXTRA_SOURCE, source) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceDividerHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceDividerHolder.kt new file mode 100644 index 000000000..b7cd0c58b --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceDividerHolder.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.ui.settings.sources + +import android.view.ViewGroup +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder + +class SourceDividerHolder(parent: ViewGroup) : + BaseViewHolder(parent, R.layout.item_sources_pref_divider) { + + override fun onBind(data: Unit, extra: Unit) = Unit +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceViewHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceViewHolder.kt new file mode 100644 index 000000000..3e706f4d3 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourceViewHolder.kt @@ -0,0 +1,15 @@ +package org.koitharu.kotatsu.ui.settings.sources + +import android.view.ViewGroup +import kotlinx.android.synthetic.main.item_source_config.* +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder + +class SourceViewHolder(parent: ViewGroup) : + BaseViewHolder(parent, R.layout.item_source_config) { + + override fun onBind(data: MangaSource, extra: Unit) { + textView_title.text = data.title + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesAdapter.kt new file mode 100644 index 000000000..4bd0d343f --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesAdapter.kt @@ -0,0 +1,59 @@ +package org.koitharu.kotatsu.ui.settings.sources + +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.item_source_config.* +import org.koin.core.KoinComponent +import org.koin.core.get +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.domain.MangaProviderFactory +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener + +class SourcesAdapter(private val onItemClickListener: OnRecyclerItemClickListener) : + RecyclerView.Adapter>(), KoinComponent { + + private val dataSet = MangaProviderFactory.sources.toMutableList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { + ITEM_SOURCE -> SourceViewHolder(parent).also(::onViewHolderCreated) + ITEM_DIVIDER -> SourceDividerHolder(parent) + else -> throw IllegalArgumentException("Unsupported viewType $viewType") + } + + override fun getItemCount() = dataSet.size + + override fun onBindViewHolder(holder: BaseViewHolder<*, Unit>, position: Int) { + (holder as? SourceViewHolder)?.bind(dataSet[position], Unit) + } + + @SuppressLint("ClickableViewAccessibility") + private fun onViewHolderCreated(holder: SourceViewHolder) { + holder.imageView_config.setOnClickListener { v -> + onItemClickListener.onItemClick(holder.requireData(), holder.adapterPosition, v) + } + holder.imageView_handle.setOnTouchListener { v, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + onItemClickListener.onItemLongClick(holder.requireData(), holder.adapterPosition, v) + } else { + false + } + } + } + + fun moveItem(oldPos: Int, newPos: Int) { + val item = dataSet.removeAt(oldPos) + dataSet.add(newPos, item) + notifyItemMoved(oldPos, newPos) + get().sourcesOrder = dataSet.map { it.ordinal } + } + + companion object { + + const val ITEM_SOURCE = 0 + const val ITEM_DIVIDER = 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesReorderCallback.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesReorderCallback.kt new file mode 100644 index 000000000..36c24b23e --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesReorderCallback.kt @@ -0,0 +1,23 @@ +package org.koitharu.kotatsu.ui.settings.sources + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView + +class SourcesReorderCallback : ItemTouchHelper.SimpleCallback(ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0) { + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val adapter = recyclerView.adapter as? SourcesAdapter ?: return false + val oldPos = viewHolder.adapterPosition + val newPos = target.adapterPosition + adapter.moveItem(oldPos, newPos) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit + + override fun isLongPressDragEnabled() = false +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt index 4ad9c7918..bf8e04e87 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt @@ -1,12 +1,47 @@ package org.koitharu.kotatsu.ui.settings.sources +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.ItemTouchHelper +import kotlinx.android.synthetic.main.fragment_settings_sources.* import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.ui.common.BaseFragment +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener +import org.koitharu.kotatsu.ui.settings.SettingsActivity -class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources) { +class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources), + OnRecyclerItemClickListener { + + private lateinit var reorderHelper: ItemTouchHelper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + reorderHelper = ItemTouchHelper(SourcesReorderCallback()) + } override fun onResume() { super.onResume() activity?.setTitle(R.string.remote_sources) } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + recyclerView.adapter = SourcesAdapter(this) + reorderHelper.attachToRecyclerView(recyclerView) + } + + override fun onDestroyView() { + reorderHelper.attachToRecyclerView(null) + super.onDestroyView() + } + + override fun onItemClick(item: MangaSource, position: Int, view: View) { + (activity as? SettingsActivity)?.openMangaSourceSettings(item) + } + + override fun onItemLongClick(item: MangaSource, position: Int, view: View): Boolean { + reorderHelper.startDrag(recyclerView.findViewHolderForAdapterPosition(position) ?: return false) + return super.onItemLongClick(item, position, view) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/PreferenceExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/PreferenceExt.kt deleted file mode 100644 index 39efea10c..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/PreferenceExt.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.koitharu.kotatsu.utils.ext - -import androidx.preference.EditTextPreference -import androidx.preference.ListPreference - -fun ListPreference.bindSummary(listener: (String) -> Boolean = { true }) { - summary = entries.getOrNull(findIndexOfValue(value)) - this.setOnPreferenceChangeListener { preference, newValue -> - newValue as String - preference as ListPreference - val res = listener(newValue) - if (res) { - preference.summary = preference.entries.getOrNull(preference.findIndexOfValue(newValue)) - } - res - } -} - -fun EditTextPreference.bindSummary( - formatter: (String) -> String = { it }, - listener: (String) -> Boolean = { true } -) { - summary = formatter(text) - this.setOnPreferenceChangeListener { preference, newValue -> - newValue as String - preference as EditTextPreference - val res = listener(newValue) - if (res) { - preference.summary = formatter(newValue) - } - res - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_reorder_handle.xml b/app/src/main/res/drawable/ic_reorder_handle.xml new file mode 100644 index 000000000..cf866f8f1 --- /dev/null +++ b/app/src/main/res/drawable/ic_reorder_handle.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings_sources.xml b/app/src/main/res/layout/fragment_settings_sources.xml index 4b6b4a9b5..8ecbd9326 100644 --- a/app/src/main/res/layout/fragment_settings_sources.xml +++ b/app/src/main/res/layout/fragment_settings_sources.xml @@ -1,6 +1,12 @@ - - \ No newline at end of file + 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:id="@+id/recyclerView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:scrollbars="vertical" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_source_config" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_source_config.xml b/app/src/main/res/layout/item_source_config.xml new file mode 100644 index 000000000..8397effaf --- /dev/null +++ b/app/src/main/res/layout/item_source_config.xml @@ -0,0 +1,40 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_sources_pref_divider.xml b/app/src/main/res/layout/item_sources_pref_divider.xml new file mode 100644 index 000000000..42bc92f79 --- /dev/null +++ b/app/src/main/res/layout/item_sources_pref_divider.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index bbddded3c..2961171d3 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -2,6 +2,7 @@ list_mode theme + sources_order pages_cache_clear -1 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6e2019b00..61f42cac6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -83,4 +83,5 @@ Standard Webtoon Read mode + Put items below to disable it \ No newline at end of file