From aec2d716887042151c514238b3443f6ec5704cfe Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 22 May 2020 20:28:14 +0300 Subject: [PATCH] Manga updates feed --- .../core/db/migrations/Migration5To6.kt | 2 +- .../koitharu/kotatsu/core/prefs/AppSection.kt | 2 +- .../koitharu/kotatsu/ui/main/MainActivity.kt | 9 ++ .../kotatsu/ui/main/list/MangaListFragment.kt | 2 +- .../ui/main/list/local/LocalListFragment.kt | 2 - .../kotatsu/ui/main/tracklogs/FeedAdapter.kt | 19 +++ .../kotatsu/ui/main/tracklogs/FeedFragment.kt | 113 ++++++++++++++++++ .../kotatsu/ui/main/tracklogs/FeedHolder.kt | 30 +++++ .../ui/main/tracklogs/FeedPresenter.kt | 40 +++++++ .../kotatsu/ui/main/tracklogs/FeedView.kt | 19 +++ app/src/main/res/drawable/ic_feed.xml | 14 +++ .../main/res/layout/fragment_tracklogs.xml | 47 ++++++++ app/src/main/res/layout/item_tracklog.xml | 76 ++++++++++++ app/src/main/res/menu/nav_drawer.xml | 4 + app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 16 files changed, 378 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedAdapter.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedFragment.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedHolder.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedPresenter.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedView.kt create mode 100644 app/src/main/res/drawable/ic_feed.xml create mode 100644 app/src/main/res/layout/fragment_tracklogs.xml create mode 100644 app/src/main/res/layout/item_tracklog.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration5To6.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration5To6.kt index 9cf52d819..13c8935f1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration5To6.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration5To6.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -object Migration5To6 : Migration(4, 5) { +object Migration5To6 : Migration(5, 6) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE IF NOT EXISTS track_logs (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, manga_id INTEGER NOT NULL, chapters TEXT NOT NULL, created_at INTEGER NOT NULL, FOREIGN KEY(manga_id) REFERENCES manga(manga_id) ON UPDATE NO ACTION ON DELETE CASCADE)") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt index 56ba0b401..64ce67264 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt @@ -2,5 +2,5 @@ package org.koitharu.kotatsu.core.prefs enum class AppSection { - LOCAL, FAVOURITES, HISTORY; + LOCAL, FAVOURITES, HISTORY, FEED } \ No newline at end of file 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 cdc4c4ec1..d3341351c 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 @@ -29,6 +29,7 @@ import org.koitharu.kotatsu.ui.main.list.favourites.FavouritesContainerFragment import org.koitharu.kotatsu.ui.main.list.history.HistoryListFragment import org.koitharu.kotatsu.ui.main.list.local.LocalListFragment import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment +import org.koitharu.kotatsu.ui.main.tracklogs.FeedFragment import org.koitharu.kotatsu.ui.reader.ReaderActivity import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.ui.settings.AppUpdateService @@ -118,6 +119,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList settings.defaultSection = AppSection.LOCAL setPrimaryFragment(LocalListFragment.newInstance()) } + R.id.nav_feed -> { + settings.defaultSection = AppSection.FEED + setPrimaryFragment(FeedFragment.newInstance()) + } R.id.nav_action_settings -> { startActivity(SettingsActivity.newIntent(this)) return true @@ -190,6 +195,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList navigationView.setCheckedItem(R.id.nav_history) setPrimaryFragment(HistoryListFragment.newInstance()) } + AppSection.FEED -> { + navigationView.setCheckedItem(R.id.nav_feed) + setPrimaryFragment(FeedFragment.newInstance()) + } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt index 3e5dd8e8a..78e1614df 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt @@ -57,7 +57,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang adapter = MangaListAdapter(this) recyclerView.setHasFixedSize(true) initListMode(settings.listMode) - recyclerView.adapter = adapter + // recyclerView.adapter = adapter recyclerView.addOnScrollListener(PaginationScrollListener(4, this)) swipeRefreshLayout.setOnRefreshListener(this) recyclerView_filter.setHasFixedSize(true) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/local/LocalListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/local/LocalListFragment.kt index a4b90ec9b..58998f245 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/local/LocalListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/local/LocalListFragment.kt @@ -102,8 +102,6 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback companion object { - private const val REQUEST_IMPORT = 50 - fun newInstance() = LocalListFragment() } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedAdapter.kt new file mode 100644 index 000000000..b5727fdde --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedAdapter.kt @@ -0,0 +1,19 @@ +package org.koitharu.kotatsu.ui.main.tracklogs + +import android.view.ViewGroup +import org.koitharu.kotatsu.core.model.TrackingLogItem +import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener + +class FeedAdapter(onItemClickListener: OnRecyclerItemClickListener? = null) : + BaseRecyclerAdapter(onItemClickListener) { + + override fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder { + return FeedHolder(parent) + } + + override fun onGetItemId(item: TrackingLogItem) = item.id + + override fun getExtra(item: TrackingLogItem, position: Int) = Unit +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedFragment.kt new file mode 100644 index 000000000..a2e68177c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedFragment.kt @@ -0,0 +1,113 @@ +package org.koitharu.kotatsu.ui.main.tracklogs + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.fragment_tracklogs.* +import moxy.MvpDelegate +import moxy.ktx.moxyPresenter +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.TrackingLogItem +import org.koitharu.kotatsu.ui.common.BaseFragment +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener +import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener +import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration +import org.koitharu.kotatsu.ui.details.MangaDetailsActivity +import org.koitharu.kotatsu.utils.ext.callOnScrollListeners +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.hasItems + +class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), FeedView, + PaginationScrollListener.Callback, OnRecyclerItemClickListener { + + private val presenter by moxyPresenter(factory = ::FeedPresenter) + + private var adapter: FeedAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + adapter = FeedAdapter(this) + recyclerView.adapter = adapter + recyclerView.addItemDecoration( + SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)) + ) + recyclerView.setHasFixedSize(true) + recyclerView.addOnScrollListener(PaginationScrollListener(4, this)) + if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) { + onRequestMoreItems(0) + } + } + + override fun onDestroyView() { + adapter = null + super.onDestroyView() + } + + override fun onListChanged(list: List) { + adapter?.replaceData(list) + if (list.isEmpty()) { + setUpEmptyListHolder() + layout_holder.isVisible = true + } else { + layout_holder.isVisible = false + } + recyclerView.callOnScrollListeners() + } + + override fun onListAppended(list: List) { + adapter?.appendData(list) + recyclerView.callOnScrollListeners() + } + + override fun onListError(e: Throwable) { + if (recyclerView.hasItems) { + Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT) + .show() + } else { + textView_holder.text = e.getDisplayMessage(resources) + textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds( + 0, + R.drawable.ic_error_large, + 0, + 0 + ) + layout_holder.isVisible = true + } + } + + override fun onError(e: Throwable) { + Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show() + } + + override fun onLoadingStateChanged(isLoading: Boolean) { + val hasItems = recyclerView.hasItems + progressBar.isVisible = isLoading && !hasItems + if (isLoading) { + layout_holder.isVisible = false + } + } + + override fun onRequestMoreItems(offset: Int) { + presenter.loadList(offset) + } + + override fun onItemClick(item: TrackingLogItem, position: Int, view: View) { + startActivity(MangaDetailsActivity.newIntent(context ?: return, item.manga)) + } + + private fun setUpEmptyListHolder() { + textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null) + textView_holder.setText(R.string.text_feed_holder) + } + + companion object { + + fun newInstance() = FeedFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedHolder.kt new file mode 100644 index 000000000..a46d26d1f --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedHolder.kt @@ -0,0 +1,30 @@ +package org.koitharu.kotatsu.ui.main.tracklogs + +import android.view.ViewGroup +import coil.api.clear +import coil.api.load +import kotlinx.android.synthetic.main.item_tracklog.* +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.TrackingLogItem +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder +import org.koitharu.kotatsu.utils.ext.format + +class FeedHolder(parent: ViewGroup) : + BaseViewHolder(parent, R.layout.item_tracklog) { + + override fun onBind(data: TrackingLogItem, extra: Unit) { + imageView_cover.load(data.manga.coverUrl) { + placeholder(R.drawable.ic_placeholder) + fallback(R.drawable.ic_placeholder) + error(R.drawable.ic_placeholder) + } + textView_title.text = data.manga.title + textView_subtitle.text = data.createdAt.format("d.m.Y") + textView_chapters.text = data.chapters.joinToString("\n") + } + + override fun onRecycled() { + super.onRecycled() + imageView_cover.clear() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedPresenter.kt new file mode 100644 index 000000000..c865efd5c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedPresenter.kt @@ -0,0 +1,40 @@ +package org.koitharu.kotatsu.ui.main.tracklogs + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import moxy.presenterScope +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.domain.tracking.TrackingRepository +import org.koitharu.kotatsu.ui.common.BasePresenter + +class FeedPresenter : BasePresenter() { + + private lateinit var repository: TrackingRepository + + override fun onFirstViewAttach() { + repository = TrackingRepository() + super.onFirstViewAttach() + } + + fun loadList(offset: Int) { + presenterScope.launch { + viewState.onLoadingStateChanged(true) + try { + val list = withContext(Dispatchers.IO) { + repository.getTrackingLog(offset, 20) + } + viewState.onListChanged(list) + } catch (e: CancellationException) { + } catch (e: Throwable) { + if (BuildConfig.DEBUG) { + e.printStackTrace() + } + viewState.onListError(e) + } finally { + viewState.onLoadingStateChanged(false) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedView.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedView.kt new file mode 100644 index 000000000..88bb6ecfd --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/tracklogs/FeedView.kt @@ -0,0 +1,19 @@ +package org.koitharu.kotatsu.ui.main.tracklogs + +import moxy.viewstate.strategy.AddToEndSingleTagStrategy +import moxy.viewstate.strategy.AddToEndStrategy +import moxy.viewstate.strategy.StateStrategyType +import org.koitharu.kotatsu.core.model.TrackingLogItem +import org.koitharu.kotatsu.ui.common.BaseMvpView + +interface FeedView : BaseMvpView { + + @StateStrategyType(AddToEndSingleTagStrategy::class, tag = "content") + fun onListChanged(list: List) + + @StateStrategyType(AddToEndStrategy::class, tag = "content") + fun onListAppended(list: List) + + @StateStrategyType(AddToEndSingleTagStrategy::class, tag = "content") + fun onListError(e: Throwable) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_feed.xml b/app/src/main/res/drawable/ic_feed.xml new file mode 100644 index 000000000..fdae2d8d3 --- /dev/null +++ b/app/src/main/res/drawable/ic_feed.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/layout/fragment_tracklogs.xml b/app/src/main/res/layout/fragment_tracklogs.xml new file mode 100644 index 000000000..a5fa8e6fd --- /dev/null +++ b/app/src/main/res/layout/fragment_tracklogs.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_tracklog.xml b/app/src/main/res/layout/item_tracklog.xml new file mode 100644 index 000000000..e691b87e8 --- /dev/null +++ b/app/src/main/res/layout/item_tracklog.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/nav_drawer.xml b/app/src/main/res/menu/nav_drawer.xml index 6df31b518..75869c7a7 100644 --- a/app/src/main/res/menu/nav_drawer.xml +++ b/app/src/main/res/menu/nav_drawer.xml @@ -14,6 +14,10 @@ android:id="@+id/nav_history" android:icon="@drawable/ic_history" android:title="@string/history" /> + Всё избранное В этой категории ничего нет Прочитать позже + Обновления + Here you will see manga updates \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 04f54363d..d9f3e96f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -136,4 +136,6 @@ All favourites This category is empty Read later + Updates + Here you will see manga updates \ No newline at end of file